Repository: jamiebuilds/babel-handbook
Branch: master
Commit: c6828415127f
Files: 105
Total size: 2.7 MB
Directory structure:
gitextract__u4zmoof/
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── crowdin.yaml
├── package.json
└── translations/
├── af/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── ar/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── ca/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── cs/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── da/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── de/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── el/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── en/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── es-ES/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── fa-IR/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── fi/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── fr/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── he/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── hu/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── id-ID/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── it/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── ja/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── ko/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── nl/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── no/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── pl/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── pt-BR/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── pt-PT/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── ro/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── ru/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── sk-SK/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── sr/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── sv-SE/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── tr/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── uk/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── vi/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
├── zh-Hans/
│ ├── README.md
│ ├── plugin-handbook.md
│ └── user-handbook.md
└── zh-Hant/
├── README.md
├── plugin-handbook.md
└── user-handbook.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules
*.log
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
### Translating the handbook
The English translation of this handbook is maintained in this repository, for
other translations we use [Crowdin](https://crowdin.com/), if you would like to
contribute to one of the translations you can invite yourself to the Crowdin
Project [here](https://crowdin.com/project/babel-plugin-handbook/invite).
Changes to the English translation will automatically be sent to Crowdin which
will notify translators of changes. When translations are completed, they will be
built and sent back to GitHub. There's a short delay, but if any part of this
process does not happen, please open an issue.
Once you set up your account on Crowdin you should be taken to
[this page](https://crowdin.com/project/babel-plugin-handbook).
Click on the language you would like to contribute to and then click on
"README.md". You will be taken to the translation editor.

On the left side you can see the handbook. In red are missing translations,
light green are translated, and dark green are translated and verified.
If you click on one of the sections, it will bring it up in the center panel
where you can enter a translation and save it.
If you click on a section with text formatting or links, Crowdin has a special
syntax for matching up the formatting.

If you match the numbered `<0>...0>` delimiters, Crowdin will build the final
translation with all the formatting and links of the original.
Once a translation is finished, it should only be a few minutes before the
changes get pushed to GitHub automatically.
================================================
FILE: LICENSE
================================================
Creative Commons Attribution 4.0 International
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More_considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution 4.0 International Public License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution 4.0 International Public License ("Public License"). To the
extent this Public License may be interpreted as a contract, You are
granted the Licensed Rights in consideration of Your acceptance of
these terms and conditions, and the Licensor grants You such rights in
consideration of benefits the Licensor receives from making the
Licensed Material available under these terms and conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
d. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
e. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
f. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
g. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
h. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
i. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
j. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
k. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
4. If You Share Adapted Material You produce, the Adapter's
License You apply must not prevent recipients of the Adapted
Material from complying with this Public License.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material; and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public licenses.
Notwithstanding, Creative Commons may elect to apply one of its public
licenses to material it publishes and in those instances will be
considered the "Licensor." Except for the limited purpose of indicating
that material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the public
licenses.
Creative Commons may be contacted at creativecommons.org.
================================================
FILE: README.md
================================================
# Babel Handbook
Written by [Jamie Kyle](https://jamie.build/)
A guided handbook on how to use Babel and how to create plugins for Babel.
**Translations**
- [English](https://github.com/thejameskyle/babel-handbook/tree/master/translations/en/README.md)
- [Afrikaans](https://github.com/thejameskyle/babel-handbook/tree/master/translations/af/README.md)
- [العربية](https://github.com/thejameskyle/babel-handbook/tree/master/translations/ar/README.md)
- [Català](https://github.com/thejameskyle/babel-handbook/tree/master/translations/ca/README.md)
- [Čeština](https://github.com/thejameskyle/babel-handbook/tree/master/translations/cs/README.md)
- [Dansk](https://github.com/thejameskyle/babel-handbook/tree/master/translations/da/README.md)
- [Deutsch](https://github.com/thejameskyle/babel-handbook/tree/master/translations/de/README.md)
- [Ελληνικά](https://github.com/thejameskyle/babel-handbook/tree/master/translations/el/README.md)
- [Español](https://github.com/thejameskyle/babel-handbook/tree/master/translations/es-ES/README.md)
- [فارسی](https://github.com/thejameskyle/babel-handbook/tree/master/translations/fa-IR/README.md)
- [Français](https://github.com/thejameskyle/babel-handbook/tree/master/translations/fr/README.md)
- [עִברִית](https://github.com/thejameskyle/babel-handbotree/master/ok/translations/he/README.md)
- [Italiano](https://github.com/thejameskyle/babel-handbook/tree/master/translations/it/README.md)
- [日本語](https://github.com/thejameskyle/babel-handbook/tree/master/translations/ja/README.md)
- [한국어](https://github.com/thejameskyle/babel-handbook/tree/master/translations/ko/README.md)
- [Magyar](https://github.com/thejameskyle/babel-handbook/tree/master/translations/hu/README.md)
- [Nederlands](https://github.com/thejameskyle/babel-handbook/tree/master/translations/nl/README.md)
- [Norsk](https://github.com/thejameskyle/babel-handbook/tree/master/translations/no/README.md)
- [Polskie](https://github.com/thejameskyle/babel-handbook/tree/master/translations/pl/README.md)
- [Português](https://github.com/thejameskyle/babel-handbook/tree/master/translations/pt-PT/README.md)
- [Português (Brasil)](https://github.com/thejameskyle/babel-handbook/tree/master/translations/pt-BR/README.md)
- [Română](https://github.com/thejameskyle/babel-handbook/tree/master/translations/ro/README.md)
- [Русский](https://github.com/thejameskyle/babel-handbook/tree/master/translations/ru/README.md)
- [Српски језик (Ћирилица)](https://github.com/thejameskyle/babel-handbook/tree/master/translations/sr/README.md)
- [Suomi](https://github.com/thejameskyle/babel-handbook/tree/master/translations/fi/README.md)
- [Svenska](https://github.com/thejameskyle/babel-handbook/tree/master/translations/sv-SE/README.md)
- [Türkçe](https://github.com/thejameskyle/babel-handbook/tree/master/translations/tr/README.md)
- [Tiếng Việt](https://github.com/thejameskyle/babel-handbook/tree/master/translations/vi/README.md)
- [Українська](https://github.com/thejameskyle/babel-handbook/tree/master/translations/uk/README.md)
- [中文](https://github.com/thejameskyle/babel-handbook/tree/master/translations/zh-Hans/README.md)
- [繁體中文](https://github.com/thejameskyle/babel-handbook/tree/master/translations/zh-Hant/README.md)
**[Request another translation](https://github.com/thejameskyle/babel-plugin-handbook/issues/new?title=Translation%20Request:%20[Please%20enter%20language%20here]&body=I%20am%20able%20to%20translate%20this%20language%20[yes/no])**
If you are reading a non-English translation of this document you will find a
number of English words that are programming concepts. If these were translated
to other languages there would be a lack of consistency and fluency when reading
about them. In many cases you will find the literal translation followed by the
English term in parenthesis `()`. For example: Abstract Syntax Trees (ASTs).
Special thanks to [@sebmck](https://github.com/sebmck/),
[@hzoo](https://github.com/hzoo),
[@jdalton](https://github.com/jdalton),
[@abraithwaite](https://github.com/abraithwaite),
[@robey](https://github.com/robey), and others for their
amazing help on this handbook.
================================================
FILE: crowdin.yaml
================================================
project_identifier: babel-plugin-handbook
api_key_env: CROWDIN_API_KEY
base_path: .
files:
- source: '/translations/en/**/*.md'
translation: '/translations/%locale%/**/%original_file_name%'
languages_mapping:
locale:
'af': 'af'
'ar': 'ar'
'ca': 'ca'
'cs': 'cs'
'da': 'da'
'de': 'de'
'el': 'el'
'es-ES': 'es-ES'
'fi': 'fi'
'fr': 'fr'
'he': 'he'
'hu': 'hu'
'it': 'it'
'ja': 'ja'
'ko': 'ko'
'no': 'no'
'nl': 'nl'
'pl': 'pl'
'pt-BR': 'pt-BR'
'pt-PT': 'pt-PT'
'ro': 'ro'
'ru': 'ru'
'sr': 'sr'
'sv-SE': 'sv-SE'
'tr': 'tr'
'uk': 'uk'
'vi': 'vi'
'zh-CN': 'zh-Hans'
'zh-TW': 'zh-Hant'
================================================
FILE: package.json
================================================
{
"name": "babel-handbook",
"version": "1.0.0",
"description": "How to Babel",
"repository": "thejameskyle/babel-handbook.git",
"homepage": "https://github.com/thejameskyle/babel-handbook",
"keywords": [
"documentation",
"guide",
"handbook",
"babel",
"plugin"
],
"author": "James Kyle ",
"license": "cc-by-4.0"
}
================================================
FILE: translations/af/README.md
================================================
# Babel Handbook
This handbook is divided into two parts:
* [User Handbook](user-handbook.md) - How to setup/configure Babel and more.
* [Plugin Handbook](plugin-handbook.md) - How to create plugins for Babel.
> For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.
If you are reading a non-english translation of this handbook you may still find english sections that have not yet been translated. If you would like to contribute to one of the translations you must do so through Crowdin. Please read the [contributing guidelines](/CONTRIBUTING.md) for more information. You will find a number of english words that are programming concepts. If these were translated to other languages there would be a lack of consistency and fluency when reading about them. In many cases you will find the literal translation followed by the english term in parenthesis `()`. For example: Abstract Syntax Trees (ASTs).
================================================
FILE: translations/af/plugin-handbook.md
================================================
# Babel Plugin Handbook
This document covers how to create [Babel](https://babeljs.io) [plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduction](#toc-introduction)
* [Basics](#toc-basics)
* [ASTs](#toc-asts)
* [Stages of Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Lexical Analysis](#toc-lexical-analysis)
* [Syntactic Analysis](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Writing your first Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introduction
Babel is a generic multi-purpose compiler for JavaScript. More than that it is a collection of modules that can be used for many different forms of static analysis.
> Static analysis is the process of analyzing code without executing it. (Analysis of code while executing it is known as dynamic analysis). The purpose of static analysis varies greatly. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
You can use Babel to build many different types of tools that can help you be more productive and write better programs.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Basics
Babel is a JavaScript compiler, specifically a source-to-source compiler, often called a "transpiler". This means that you give Babel some JavaScript code, Babel modifies the code, and generates the new code back out.
## ASTs
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Stages of Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Parse
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntactic Analysis
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transform
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Writing your first Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/af/user-handbook.md
================================================
# Babel User Handbook
This document covers everything you ever wanted to know about using [Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduction](#toc-introduction)
* [Setting up Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Introduction
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can use (and create) the next generation of JavaScript, as well as the next generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Setting up Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
================================================
FILE: translations/ar/README.md
================================================
# Babel Handbook
This handbook is divided into two parts:
* [User Handbook](user-handbook.md) - How to setup/configure Babel and more.
* [Plugin Handbook](plugin-handbook.md) - How to create plugins for Babel.
> For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.
If you are reading a non-english translation of this handbook you may still find english sections that have not yet been translated. If you would like to contribute to one of the translations you must do so through Crowdin. Please read the [contributing guidelines](/CONTRIBUTING.md) for more information. You will find a number of english words that are programming concepts. If these were translated to other languages there would be a lack of consistency and fluency when reading about them. In many cases you will find the literal translation followed by the english term in parenthesis `()`. For example: Abstract Syntax Trees (ASTs).
================================================
FILE: translations/ar/plugin-handbook.md
================================================
# Babel Plugin Handbook
This document covers how to create [Babel](https://babeljs.io) [plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduction](#toc-introduction)
* [Basics](#toc-basics)
* [ASTs](#toc-asts)
* [Stages of Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Lexical Analysis](#toc-lexical-analysis)
* [Syntactic Analysis](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions](#toc-definitions)
* [Builders](#toc-builders)
* [المدقق](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Writing your first Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introduction
Babel is a generic multi-purpose compiler for JavaScript. More than that it is a collection of modules that can be used for many different forms of static analysis.
> Static analysis is the process of analyzing code without executing it. (Analysis of code while executing it is known as dynamic analysis). The purpose of static analysis varies greatly. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
You can use Babel to build many different types of tools that can help you be more productive and write better programs.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Basics
Babel is a JavaScript compiler, specifically a source-to-source compiler, often called a "transpiler". This means that you give Babel some JavaScript code, Babel modifies the code, and generates the new code back out.
## ASTs
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Stages of Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Parse
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntactic Analysis
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transform
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### المدقق
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Writing your first Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/ar/user-handbook.md
================================================
# Babel User Handbook
This document covers everything you ever wanted to know about using [Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduction](#toc-introduction)
* [Setting up Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Introduction
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can use (and create) the next generation of JavaScript, as well as the next generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Setting up Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
================================================
FILE: translations/ca/README.md
================================================
# Manual de Babel
Aquest manual està dividit en dues parts:
* [Manual d'usuari](user-handbook.md) - Com configurar Babel i més.
* [Manual d'extensions](plugin-handbook.md) - Com crear extensions per Babel.
> Per a futures actualitzacions, segueix a [@*thejameskyle](https://twitter.com/thejameskyle) en Twitter.
Si estàs llegint una traducció d'aquest manual, és possible que trobis seccions en anglès que encara no han estat traduïdes. Si desitja contribuir a una de les traduccions haurà de fer-ho a través de Crowdin. Per favor, llegeix les [directrius per contribuir](/CONTRIBUTING.md) per obtenir més informació. Trobaràs diverses paraules en anglès que es corresponen amb conceptes de programació. Si es traduïssin aquestes paraules a altres idiomes hi hauria una falta de consistència i fluïdesa en llegir una altra informació sobre elles. En molts casos trobaràs la traducció literal seguida del terme en anglès entre parèntesi `()`. Per exemple: Arbres de sintaxi abstracta (ASTs).
================================================
FILE: translations/ca/plugin-handbook.md
================================================
# Babel Plugin Handbook
This document covers how to create [Babel](https://babeljs.io) [plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduction](#toc-introduction)
* [Basics](#toc-basics)
* [ASTs](#toc-asts)
* [Stages of Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Lexical Analysis](#toc-lexical-analysis)
* [Syntactic Analysis](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definicions](#toc-definitions)
* [Builders](#toc-builders)
* [Validadors](#toc-validators)
* [Convertidors](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Escriure el primer Plugin de Babel](#toc-writing-your-first-babel-plugin)
* [Operacions de transformació](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulació](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Àmbit d'aplicació](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Opcions de plugin](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Millors pràctiques](#toc-best-practices)
* [Evitar travessar l'AST tant com sigui possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introducció
Babel és un compilador genèric polivalent per a JavaScript. Més que això, és una col·lecció de mòduls que poden ser utilitzats per moltes formes diferents d'anàlisi estàtic.
> L'anàlisi estàtic és el procés d'analitzar el codi sense executar-lo. (anàlisi de codi i executar-lo s'en diu anàlisi dinàmic). The purpose of static analysis varies greatly. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
You can use Babel to build many different types of tools that can help you be more productive and write better programs.
> ***Per a futures actualitzacions, segueix a [@*thejameskyle](https://twitter.com/thejameskyle) en Twitter.***
* * *
# Basics
Babel is a JavaScript compiler, specifically a source-to-source compiler, often called a "transpiler". This means that you give Babel some JavaScript code, Babel modifies the code, and generates the new code back out.
## ASTs
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Stages of Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Parse
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntactic Analysis
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transform
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definicions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validadors
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Convertidors
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Escriure el primer Plugin de Babel
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Operacions de transformació
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulació
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Àmbit d'aplicació
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Opcions de plugin
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Millors pràctiques
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Evitar travessar l'AST tant com sigui possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/ca/user-handbook.md
================================================
# Manual d'usuari de Babel
Aquest document abasta tot el que sempre ha volgut saber sobre com utilitzar [Babel](https://babeljs.io) i les seves eines relacionades.
[](http://creativecommons.org/licenses/by/4.0/)
Aquest manual està disponible en altres idiomes, vegi el [README](/README.md) per a una llista completa.
# Taula de Continguts
* [Introducció](#toc-introduction)
* [Iniciant Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Executar Babel CLI dins d'un projecte](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configurant Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configurant Babel (Avançat)](#toc-configuring-babel-advanced)
* [Especificar extensions manualment](#toc-manually-specifying-plugins)
* [Opcions d'extensions](#toc-plugin-options)
* [Personalitzant Babel basat en l'entorn](#toc-customizing-babel-based-on-environment)
* [Fent el seu propi preset](#toc-making-your-own-preset)
* [Babel i altres eines](#toc-babel-and-other-tools)
* [Eines d'anàlisi estàtica](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Estil de codi](#toc-code-style)
* [Documentació](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Editors de text i IDEs](#toc-text-editors-and-ides)
* [Suport de Babel](#toc-babel-support)
* [Fòrum de Babel](#toc-babel-forum)
* [Xat de Babel](#toc-babel-chat)
* [Problemes en Babel](#toc-babel-issues)
* [Creant un sorprenent report de bugs per Babel](#toc-creating-an-awesome-babel-bug-report)
# Introducció
Babel és un compilador multi propòsit per JavaScript. Amb Babel vostè pot utilitzar (i crear) la següent generació de Javascript, així com la següent generació d'eines per Javascript.
Javascript és un llenguatge en constant evolució, amb noves especificacions i futures propostes arribant amb noves funcionalitats de manera continua. Utilitzant Babel li permetrà fer ús de moltes d'aquestes característiques anys abans de que estiguin disponibles a tot arreu.
Babel compila el codi escrit en Javascript amb els últims estàndards a una versió que funcionarà a tot arreu. Aquest procés és conegut com a compilació source-to-source, també conegut com transpiling.
Per exemple, Babel pot transformar la nova funció fletxa d'ES2015 en:
```js
const square = n => n * n;
```
En això:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***Per a futures actualitzacions, segueix a [@*thejameskyle](https://twitter.com/thejameskyle) en Twitter.***
* * *
# Iniciant Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Executar Babel CLI dins d'un projecte
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configurant Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configurant Babel (Avançat)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Especificar extensions manualment
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Opcions d'extensions
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Personalitzant Babel basat en l'entorn
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Fent el seu propi preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel i altres eines
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Eines d'anàlisi estàtica
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Estil de codi
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentació
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Editors de text i IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Suport de Babel
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Fòrum de Babel
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Xat de Babel
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Problemes en Babel
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creant un sorprenent report de bugs per Babel
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***Per a futures actualitzacions, segueix a [@*thejameskyle](https://twitter.com/thejameskyle) en Twitter.***
================================================
FILE: translations/cs/README.md
================================================
# Babel příručka
Tato příručka je rozdělena do dvou částí:
* [Uživatelská příručka](user-handbook.md) - Jak nainstalovat/konfigurovat Babel a další.
* [Plugin příručka](plugin-handbook.md) - Jak vytvářet pluginy pro Babel.
> Aktualizace sledujte na Twitteru: [@thejameskyle](https://twitter.com/thejameskyle).
Pokud čtete neanglický překlad této příručky, stále můžete najít anglické části, které nebyly dosud přeloženy. Pokud byste chtěli přispět k jednomu z překladů musíte tak učinit prostřednictvím Crowdin. Přečtěte si prosím [pokyny pro přispívání](/CONTRIBUTING.md) pro další informace. Některé anglické výrazy vyjadřují programovací koncepty. Pokud by tyto výrazy byly přeloženy do jiných jazyků, vznikla by nekonzistence a čtení o nich by ztatilo plynulost. V mnoha případech najdete doslovný překlad, který následuje anglický výraz v závorkách `()`. Například: abstraktní syntaktické stromy (AST).
================================================
FILE: translations/cs/plugin-handbook.md
================================================
# Příručka pro pluginy Babelu
Tento dokument popisuje, jak vytvořit [Babel](https://babeljs.io) [pluginy](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
Tato příručka je k dispozici i v dalších jazycích, úplný seznam naleznete v [souboru README](/README.md).
# Obsah
* [Úvod](#toc-introduction)
* [Základy](#toc-basics)
* [AST](#toc-asts)
* [Fáze Babelu](#toc-stages-of-babel)
* [Analýza](#toc-parse)
* [Lexikální analýza](#toc-lexical-analysis)
* [Syntaktická analýza](#toc-syntactic-analysis)
* [Transformace](#toc-transform)
* [Generování](#toc-generate)
* [Průchod](#toc-traversal)
* [Inspektoři](#toc-visitors)
* [Cesty](#toc-paths)
* [Cesty v inspektorech](#toc-paths-in-visitors)
* [Stav](#toc-state)
* [Rozsahy](#toc-scopes)
* [Vazby](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definice](#toc-definitions)
* [Builders](#toc-builders)
* [Validátory](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Psaní prvního pluginu pro Babel](#toc-writing-your-first-babel-plugin)
* [Transformační operace](#toc-transformation-operations)
* [Inspekce](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulace](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Rozsah](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Osvědčené postupy](#toc-best-practices)
* [Vyhněte se co nejvíce procházení AST](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Slučujte inspektory kdykoli je to možné](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimalizace vnořených inspektorů](#toc-optimizing-nested-visitors)
* [Uvědomte si vnořené struktury](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Úvod
Babel je obecný víceúčelový kompilátor pro JavaScript. Navíc je to kolekce modulů, které mohou být použity pro mnoho různých forem statické analýzy.
> Statická analýza je proces analýzy kódu bez jeho spuštění. (Analýza kódu s jeho spuštěním se nazývá Dynamická analýza). Účel statické analýzy může být různý. Používá se pro kontrolu, kompilaci, zvýrazňování, transformaci, optimalizaci, minimalizaci kódu a mnoho dalšího.
Babel můžete použít k vybudování mnoha různých typů nástrojů, které vám pomohou být produktivnější a psát lepší programy.
> ***Aktualizace sledujte na Twitteru: [@thejameskyle](https://twitter.com/thejameskyle).***
* * *
# Základy
Babel je kompilátor JavaScriptu, který kompiluje ze zdrojového kódu na zdrojový kód, často nazývaný "transpiler". To znamená, že Babelu předáte kód v JavaScriptu, Babel ho upraví a generuje nový kód zpět.
## AST
Každý z těchto kroků obsahuje vytváření a práci s [Abstraktním syntaktickým stromem](https://en.wikipedia.org/wiki/Abstract_syntax_tree) nebo-li AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Podívejte se na [AST Explorer](http://astexplorer.net/) pro získání lepšího přehledu o AST uzlech. [Zde](http://astexplorer.net/#/Z1exs6BWMq) je na něj odkaz s výše uvedeným příkladem kódu.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Nebo jako objekt JavaScriptu takto:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Všimněte si, že každá úroveň AST má podobnou strukturu:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Poznámka: Některé vlastnosti byly pro jednoduchost odstraněny.
Každý z nich je známý jako **uzel**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Fáze Babelu
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Analýza
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexikální analýza
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntaktická analýza
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transformace
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generování
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Průchod
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Inspektoři
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Cesty
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Cesty v inspektorech
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Stav
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Rozsahy
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Vazby
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definice
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validátory
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Psaní prvního pluginu pro Babel
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformační operace
## Inspekce
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulace
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Rozsah
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Osvědčené postupy
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Vyhněte se co nejvíce procházení AST
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Slučujte inspektory kdykoli je to možné
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimalizace vnořených inspektorů
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Uvědomte si vnořené struktury
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/cs/user-handbook.md
================================================
# Babel User Handbook
This document covers everything you ever wanted to know about using [Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
Tato příručka je k dispozici i v dalších jazycích, úplný seznam naleznete v [souboru README](/README.md).
# Obsah
* [Úvod](#toc-introduction)
* [Setting up Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Úvod
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can use (and create) the next generation of JavaScript, as well as the next generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***Aktualizace sledujte na Twitteru: [@thejameskyle](https://twitter.com/thejameskyle).***
* * *
# Setting up Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***Aktualizace sledujte na Twitteru: [@thejameskyle](https://twitter.com/thejameskyle).***
================================================
FILE: translations/da/README.md
================================================
# Babel Handbook
This handbook is divided into two parts:
* [User Handbook](user-handbook.md) - How to setup/configure Babel and more.
* [Plugin Handbook](plugin-handbook.md) - How to create plugins for Babel.
> For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.
If you are reading a non-english translation of this handbook you may still find english sections that have not yet been translated. If you would like to contribute to one of the translations you must do so through Crowdin. Please read the [contributing guidelines](/CONTRIBUTING.md) for more information. You will find a number of english words that are programming concepts. If these were translated to other languages there would be a lack of consistency and fluency when reading about them. In many cases you will find the literal translation followed by the english term in parenthesis `()`. For example: Abstract Syntax Trees (ASTs).
================================================
FILE: translations/da/plugin-handbook.md
================================================
# Babel Plugin Handbook
This document covers how to create [Babel](https://babeljs.io) [plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduction](#toc-introduction)
* [Basics](#toc-basics)
* [ASTs](#toc-asts)
* [Stages of Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Lexical Analysis](#toc-lexical-analysis)
* [Syntactic Analysis](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Writing your first Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introduction
Babel is a generic multi-purpose compiler for JavaScript. More than that it is a collection of modules that can be used for many different forms of static analysis.
> Static analysis is the process of analyzing code without executing it. (Analysis of code while executing it is known as dynamic analysis). The purpose of static analysis varies greatly. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
You can use Babel to build many different types of tools that can help you be more productive and write better programs.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Basics
Babel is a JavaScript compiler, specifically a source-to-source compiler, often called a "transpiler". This means that you give Babel some JavaScript code, Babel modifies the code, and generates the new code back out.
## ASTs
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Stages of Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Parse
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntactic Analysis
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transform
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Writing your first Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/da/user-handbook.md
================================================
# Babel User Handbook
This document covers everything you ever wanted to know about using [Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduction](#toc-introduction)
* [Setting up Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Introduction
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can use (and create) the next generation of JavaScript, as well as the next generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Setting up Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
================================================
FILE: translations/de/README.md
================================================
# Babel Handbuch
Das Handbuch besteht aus zwei Teilen:
* [Nutzerhandbuch](user-handbook.md) - Babel aufsetzen, konfigurieren und mehr.
* [Plugin-Handbuch](plugin-handbook.md) - Plugins für Babel erstellen.
> For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.
Beim Lesen einer Übersetzung dieses Dokumentes, kann es sein, dass einige Kapitel noch nicht aus dem Englischen übersetzt worden sind. Um bei der Übersetzung mitzuhelfen kann dies mittels Crowdin durchgeführt werden. Für weitere Informationen bitte die [Regeln zur Mitarbeit](/CONTRIBUTING.md) lesen. You will find a number of english words that are programming concepts. If these were translated to other languages there would be a lack of consistency and fluency when reading about them. In many cases you will find the literal translation followed by the english term in parenthesis `()`. For example: Abstract Syntax Trees (ASTs).
================================================
FILE: translations/de/plugin-handbook.md
================================================
# Babel Plugin Handbook
Dieses Dokument beschreibt das Erstellen von [Babel](https://babeljs.io) [Plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
Das Handbuch ist in verschiedenen Sprachen verfügbar. Eine vollständige Liste findest du im [README](/README.md).
# Table of Contents
* [Einführung](#toc-introduction)
* [Basics](#toc-basics)
* [ASTs](#toc-asts)
* [Stages of Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Lexical Analysis](#toc-lexical-analysis)
* [Syntactic Analysis](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Writing your first Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Einführung
Babel ist ein generischer Compiler für JavaScript. Des weiteren ist es eine Sammlung von Modulen, welche für vielfältige Arten von statischer Analyse benutzt werden können.
> Statische Analyse ist die Analyse von Code ohne ihn auszuführen. (Analyse von Code mit Ausführung desselben wird als dynamische Analyse bezeichnet). Die statische Analyse kann vielfältige Anwendungen haben. Sie kann für Linting, Kompilieren, Syntax-Highlighting, Codetransformation, Optimierung, Minifikation und vielen anderen Anwendungen verwendet werden.
Babel kann zur erstellung von verschiedenen Arten von Tools verwedet werden um produktiver zu werden und bessere Programme zu schreiben.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Basics
Babel ist ein JavaScript Compiler, genauer ein Source zu Source Compiler, auch "Transpiler" genannt. Das heißt, dass Babel als Eingabe JavsScript Code bekommt, den Code modifiziert und neuen Code als Ausgabe produziert.
## ASTs
In jeden dieser Schritte ist das Erstellen oder das Arbeiten mit einem [Abstrakten Syntaxbaum](https://en.wikipedia.org/wiki/Abstract_syntax_tree) oder AST enthalten.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Um mit AST Knoten zu experimentieren kann das Tool [AST Explorer](http://astexplorer.net/) verwendet werden. Dieser [Link](http://astexplorer.net/#/Z1exs6BWMq) enthält den AST für den Beispielcode von oben.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Oder auch als JavaScript Objekt:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Jede Ebene des AST hat eine ähnliche Struktur:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Hinweis: Der Einfachheit halber wurden einige Eigenschaften entfernt.
Jede dieser Ebenen wird auch als **Knoten** bezeichnet. Ein AST kann aus einem einzelnen Knoten oder hunderten wenn nicht tausenden von Knoten bestehen. Zusammen können sie die Syntax eines Programmes beschreiben und können für die statische Analyse verwendet werden.
Jeder Knoten hat das folgende Interface:
```typescript
interface Node {
type: string;
}
```
Das `type` Feld ist ein String welcher die Art des Knotens repräsentiert (z.B. `"FunctionDeclaration"`, `"Identifier"`, oder `"BinaryExpression"`). Jeder Knotentyp definiert zusätzliche Eigenschaften welche den speziellen Knotentypen genauer beschreiben.
Babel generiert zusätzliche Eigenschaften für jeden Knoten, welche die Position des Knotens im originalen Source Code beschreiben.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Die Eigenschaften `start`, `end` und `loc` werden zu jedem Knoten hinzugefügt.
## Stages of Babel
Die drei grundsätzlichen Phasen von Babel sind **Parsen(parse)**, **Transformieren(transform)** und **Generieren(generate)**.
### Parse
Die **parse** Phase erhält Source Code als Eingabe und produziert einen AST als Ausgabe. Die Phase wird in zwei Schritte aufgeteilt: [**Lexikalische Analyse**](https://en.wikipedia.org/wiki/Lexical_analysis) und [**Syntaktische Analyse**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexikalische Analyse nimmt einen Source Code String und wandelt ihn in einen Strom von **Tokens** um.
Man kann sich die Tokens als eine Liste von syntaktischen Sprachelementen vorstellen.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Jeder `type` hat eine Menge von Eigenschaften welche das Token beschreiben:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Analog zu AST Knoten haben Tokens auch eine `start`, `end` und `loc` Eigenschaft.
#### Syntactic Analysis
Syntaktische Analyse wandelt einen Strom von Tokens in einen AST um. Diese Phase verwendet die Informationen welche in den Tokens vorhanden ist und wandelt diese in einen AST um, welcher die Struktur des Codes so darstellt welche einfacher bearbeitet werden kann.
### Transform
Die [Transformations](https://en.wikipedia.org/wiki/Program_transformation)-Phase traversiert einen AST und fügt neue Knoten hinzu, ändert vorhandene Knoten ab oder entfernt Knoten. Dies ist der komplexeste Schritt von Babel oder anderen Compilern. Das ist auch die Phase in welcher Plugins eingesetzt weden und ist somit ein großer Bestandteil dieses Handbuches. Deshalb werden wir diese Phase nicht weiter vertiefen.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Die Code Generierung ist ziemlich einfach: Der AST wird mit einer Tiefensuche traversiert. Dabei wird eine Zeichenkette generiert, welche den transformierten Code darstellt.
## Traversal
Um einen AST zu transformieren muss der Baum rekursiv [durchlaufen](https://en.wikipedia.org/wiki/Tree_traversal) werden.
Angenommen, wir haben den Typ `FunctionDeclaration`. Er hat folgende Eigenschaften: `Id`, `Params` und `Körper`. Jede diese Eigenschaften hat verschachtelte Knoten.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Writing your first Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/de/user-handbook.md
================================================
# Babel User Handbook
This document covers everything you ever wanted to know about using [Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
Das Handbuch ist in verschiedenen Sprachen verfügbar. Eine vollständige Liste findest du im [README](/README.md).
# Inhaltsverzeichnis
* [Einführung](#toc-introduction)
* [Setting up Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Einführung
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can use (and create) the next generation of JavaScript, as well as the next generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Setting up Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
================================================
FILE: translations/el/README.md
================================================
# Εγχειρίδιο Babel
Αυτό το εγχειρίδιο χωρίζεται σε δύο μέρη:
* [Εγχειρίδιο Χρήστη](user-handbook.md) - Πως να ρυθμίσετε το Babel και πολλά άλλα.
* [Εγχειρίδιο Βυσμάτος (Plugin)](plugin-handbook.md) - Πως να δημιουργήσετε βύσματα (plugins) για το Babel.
> Για μελλοντικές ενημερώσεις ακολουθήστε τον [@thejameskyle](https://twitter.com/thejameskyle) στο Twitter.
Εαν διαβάζετε μια μη-αγγλική μετάφραση του παρόντος εγχειριδίου ίσως να βρείτε τμήματα του κειμένου στα αγγλικά, τα οποία δεν έχουν μεταφραστεί ακόμη. Αν θα θέλατε να συνεισφέρετε σε μια ή περισσότερες μεταφράσεις θα πρέπει να το κάνετε μέσω του Crowdin. Για περισσότερες πληροφορίες παρακαλώ διαβάστε τις [κατευθυντήριες γραμμές συνεισφοράς](/CONTRIBUTING.md). Θα βρείτε έναν αριθμό από αγγλικές λέξεις οι οποίες είναι προγραμματιστικές έννοιες. Αν μεταφραστούν σε άλλες γλώσσες θα υπάρξει ελλειψη συνέπειας και ευχέρειας κατά την ανάγνωση τους. Σε πολλές περιπτώσεις θα βρείτε την κυριολεκτική μετάφραση τους να ακολουθείτε από ένα αγγλικό όρο μέσα σε παρένθεση `()`. Για πάραδειγμα: Αφηρημένη Δενδροειδείς Σύνταξη (ΑΔΣ).
================================================
FILE: translations/el/plugin-handbook.md
================================================
# Babel: Εγχειρίδιο για νέους χρήστες
Το παρόν έγγραφο καλύπτει πώς να δημιουργήσετε [Babel](https://babeljs.io) [plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
Το εγχειρίδιο αυτό είναι διαθέσιμο και σε άλλες γλώσσες, δείτε στο [README](/README.md) τις υπόλοιπες γλώσσες.
# ΠΙΝΑΚΑΣ ΠΕΡΙΕΧΟΜΕΝΩΝ
* [Εισαγωγή](#toc-introduction)
* [Βασικά](#toc-basics)
* [ASTs](#toc-asts)
* [Στάδια του Babel](#toc-stages-of-babel)
* [Ανάλυση](#toc-parse)
* [Λεξικολογική Ανάλυση](#toc-lexical-analysis)
* [Συντακτική Ανάλυση](#toc-syntactic-analysis)
* [Μετατροπή](#toc-transform)
* [Παραγωγή](#toc-generate)
* [Διάσχιση](#toc-traversal)
* [Επισκέπτες](#toc-visitors)
* [Καθοδήγηση](#toc-paths)
* [Καθοδήγηση για τους επισκέπτες](#toc-paths-in-visitors)
* [Κατάσταση](#toc-state)
* [Εμβέλειες](#toc-scopes)
* [Συνδέσεις](#toc-bindings)
* [Διασύνδεση Προγραμματισμού Εφαρμογών](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Ορισμοί](#toc-definitions)
* [Οικοδόμοι](#toc-builders)
* [Διαδικασίες επικύρωσης](#toc-validators)
* [Μετατροπείς](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Γράφοντας το πρώτο σας Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Λειτουργίες μετατροπής](#toc-transformation-operations)
* [Επισκεψιμότητα](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Επιλογές βυσμάτων (plugins)](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Χτίζοντας Nodes](#toc-building-nodes)
* [Βέλτιστες πρακτικές](#toc-best-practices)
* [Αποφύγετε όσο το δυνατόν την διάσχιση των AST](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Συγχωνεύστε τους επισκέπτες οπότε είναι δυνατό](#toc-merge-visitors-whenever-possible)
* [Μην εκτελείτε την διαδικασία διάσχισης όταν εκτελείται χειροκίνητη αναζήτηση](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Βελτιστοποίηση των ένθετων επισκέπτών](#toc-optimizing-nested-visitors)
* [Έχοντας επίγνωση των ένθετων δομών](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Εισαγωγή
Το Babel είναι ένας γενικός μεταγλωττιστής πολλών χρήσεων για JavaScript. Πιο συγκεκριμένα, είναι μια συλλογή από ενότητες οι οποίες μπορούν να χρησιμοποιηθούν σε διαφορετικές μορφές στατικής ανάλυσης.
> Στατική ανάλυση είναι η διαδικασία ανάλυσης του κώδικα χωρίς την εκτέλεση του. (Η ανάλυση του κώδικα κατα τη διάρκεια της εκτέλεσης του είναι γνωστή και ως δυναμική ανάλυση). Ο σκοπός της στατικής ανάλυσης ποικίλλει. Μπορεί να χρησιμοποιηθεί για καθάρισμα (linting), μεταγλώττιση, έμφαση (hightlighting), διαμόρφωση, βελτιστοποίηση, σμίκρυνση και πολλά άλλα.
Μπορείτε να χρησιμοποιήσετε το Babel για να κατασκευάσετε πολλούς διαφορετικούς τύπους εργαλείων τα οποία μπορούν να σας βοηθήσουν να γίνετε πιο παραγωγικοί και να γράφετε καλύτερα προγράμματα.
> ***Για μελλοντικές ενημερώσεις μπορείτε να ακολουθήσετε τον [@thejameskyle](https://twitter.com/thejameskyle) στο Twitter.***
* * *
# Βασικά στοιχεία
Το Babel είναι ένας μεταγλωττιστής για JavaScript, πιο συγκεκριμένα ένας μεταγλωττιστής από πηγαίο κώδικα σε πηγαίο κωδικα, συχνά αποκαλούμενο «transpiler». Αυτό σημαίνει ότι δίνοντας στο Babel κάποιο κώδικα JavaScript, αυτό τροποποιεί τον κώδικα και παράγει ένα νέο κώδικα.
## AST
Κάθενα από τα βήματα συμπεριλαμβάνουν την δημιουργία ή χρήση μιας [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) εν συντομία AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Ρίξτε μια ματιά στο [AST Explorer](http://astexplorer.net/) για να πάρετε μια καλύτερη αίσθηση των AST nodes. [Εδώ](http://astexplorer.net/#/Z1exs6BWMq) θα βρείτε ένα παράδειγμα που περιέχει τον παραπάνω κώδικα.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Ή ως ένα αντικείμενο JavaScript όπως αυτό:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Θα παρατηρήσετε ότι κάθε επίπεδο του AST έχει παρόμοια δομή:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Σημείωση: Ορισμένες ιδιότητες έχουν αφαιρεθεί για απλότητα.
Κάθενα από αυτά είναι γνωστό ως **Node**. Ένα AST μπορεί να σχηματιστεί από ένα μόνο Node ή από εκατοντάδες αν όχι χιλιάδες Nodes. Μαζί είναι σε θέση να περιγράψουν την σύνταξη ενός προγράμματος, το οποίο μπορεί να χρησιμοποιηθεί για στατική ανάλυση.
Κάθε Node έχει αυτή τη μορφή:
```typescript
interface Node {
type: string;
}
```
To πέδιο `type` είναι τύπου string και αντιπροσωπεύει τον τύπο του Νοδε, δηλαδή `"FunctionDeclaration"`, `"Identifier"`, ή `"BinaryExpression"`). Κάθε τύπος Node ορίζει ένα πρόσθετο σύνολο από ιδιότητες, οι οποίες περιγράφουν το συγκεκριμένο τύπο Node.
Υπάρχουν πρόσθετες ιδιότητες για κάθε Node τις οποίες το Babel παράγει και οι οποίες περιγράφουν την θέση του Node στον αρχικό πηγαίο κώδικα.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Αυτές οι ιδιότητες `start`, `end`, `loc`, εμφανίζονται σε κάθε Node.
## Στάδια του Babel
Τα τρια αρχικά στάδια του Babel είναι **parse**, **transform**, **generate**.
### Parse
Το στάδιο **parse**, λαμβάνει τον κώδικα και εξάγει ένα AST. Υπάρχουν 2 φάσεις της ανάλυσης στο Babel: [**Λεξικολογική Ανάλύση**](https://en.wikipedia.org/wiki/Lexical_analysis) και [**Συντακτική Ανάλυση**](https://en.wikipedia.org/wiki/Parsing).
#### Λεξικολογική Ανάλυση
Η λεξικολογική ανάλυση θα λάβει μια συμβολοσειρά κώδικα (string) και θα την μετατρέψει σε ένα ρεύμα **tokens**.
Μπορείτε να σκεφτείτε τα tokens ως μια επίπεδη σειρά από συντακτικά γλωσσικά κομμάτια.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Κάθε ένας από τους `τύπους` έχει ένα σύνολο από ιδιότητες, οι οποίες περιγράφουν το κάθε token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Τα AST nodes επίσης έχουν `start`, `end`, και `loc`.
#### Συντακτική Ανάλυση
Η συντακτική ανάλυση θα πάρει ένα ρεύμα από tokens και θα τις μετατρέψει σε μια αναπαράσταση ενός AST. Χρησιμοποιώντας τις πληροφορίες από τα tokens, αυτή η φάση θα τις μορφοποιησεί σε ένα AST το οποίο αναπαρηστά την δομή του κώδικα με τέτοιο τρόπο ώστε να γίνεται ευκολότερη η διαδικασία εργασίας με αυτό.
### Transform
Το στάδιο [transform](https://en.wikipedia.org/wiki/Program_transformation) λαμβάνει ένα AST και διεισδύει μέσω αυτού στην προσθήκη, ενημέρωση και αφαίρεση nodes κατα μήκος της διείσδυσης. Αυτό είναι μακράν το πιο περίπλοκο μέρος του Babel ή οποιουδήποτε μεταγλωττιστή. Αυτό είναι το σημείο όπου τα βύσματα (plugins) λειτουργούν και αυτό θα είναι το μεγαλύτερο θέμα με το οποίο θα ασχοληθούμε σε αυτό το εγχειρίδιο. Για αυτό το λόγο δεν μπορούμε να εμβαθύνουμε αυτή τη στιγμή.
### Generate
Το στάδιο [παραγωγής κώδικα](https://en.wikipedia.org/wiki/Code_generation_(compiler)) λαμβάνει το τελικό AST και το μετατρέπει σε μια συμβολοσειρά κώδικα δημιουργώντας και [χάρτες προέλευσης](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Η παραγωγή του κώδικα είναι πολύ απλή: η διεισδυση του AST γίνεται με τον αλγόριθμο "βάθος πρώτα" (depth-first) χτίζοντας μια συμβολοσειρά η οποία αναπαρειστά τον μετασχηματισμένο κώδικα.
## Διάσχιση
Όταν θέλετε να μετασχηματίσετε ένα AST πρέπει να [διασχίσετε το δέντρο](https://en.wikipedia.org/wiki/Tree_traversal) αναδρομικά.
Ας υποθέσουμε ότι έχουμε τον τύπο `ΔήλωσηΣυνάρτησης`. Έχει μερικές ιδιότητες `id`. `params`, και `body`. Κάθενα από αυτά έχει εμφωλευμένους nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Έτσι ξεκινάμε από την `FunctionDeclaration` και γνωρίζουμε τις εσωτερικές ιδιότητες ώστε να επισκευτούμε σε σειρά κάθε μια από αυτές και τα παιδιά τους.
Επείτα πάμε στο `id` το οποίο είναι ένα απλό `identifier`. Τα `identifiers` δεν έχουν ιδιότητες παιδιών node, οπότε μπορούμε να προχωρήσουμε.
Μετέπειτα αυτού έχουμε τις `params` οι οποίες είναι μια σειρά από nodes τους οποίους επισκεπτόμαστε. Σε αυτήν την περίπτωση πρόκειται για ένα μόνο node που είναι ταυτόχρονα και `Identifier` οπότε μπρούμε να προχωρήσουμε.
Μετά βρίσκουμε το `body` το οποίο είναι ένα `BlockStatement` με την ιδιότητα `body` η οποία είναι μια σειρά από nodes έτσι πηγαίνουμε σε καθένα από αυτούς.
Το μόνο στοιχείο εδώ είναι ένα `ReturnStatement` node, το οποίο έχει ένα `argument`. Πηγαίνοντας στο `argument` βρίσκουμε ένα `BinaryExpression`.
Το `BinaryExpression` έχει έναν `operator`, έναν `αριστερά` και έναν `δεξιά`. Ο operator δεν είναι node, αλλά μια μεταβλητή, έτσι δεν χρειάζεται να πάμε σε αυτόν, αντίθετα επισκεπτόμαστε τον `αριστερά` και τον `δεξιά`.
Η διαδικασία αυτή της διάσχισης συμβαίνει σε όλο το στάδιο μετασχηματισμού της Βαβέλ.
### Visitors
Όταν λέμε "να πάμε" σε ένα κόμβο, εννοούμε πραγματικά ότι τον **επισκεπτόμαστε**. Ο λόγος που χρησιμοποιούμε αυτόν τον όρο είναι γιατί υπάρχει αυτή η έννοια ενός [**επισκέπτη**](https://en.wikipedia.org/wiki/Visitor_pattern).
Οι visitors είναι ένα μοτίβο που χρησιμοποιείται στην AST traversal διαγλωσσικά. Με απλά λόγια είναι ένα αντικείμενο με μεθόδους που ορίζονται για την αποδοχή συγκεκριμένων τύπων node σε ένα δέντρο. Αυτό είναι λίγο αφηρημένο, οπότε ας δούμε ένα παράδειγμα.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Σημείωση:** `Identifier() { ... }`είναι συντομογραφία για `Identifier: { enter() { ... } }`.
Αυτός είναι ένας βασικός visitor που όταν χρησιμοποιείται κατά τη διάρκεια μιας traversal θα καλέσει τη μέθοδο `Identifier()` για κάθε `Identifier` στο δέντρο.
Έτσι με αυτόν τον κώδικα η `Identifier()` μέθοδος θα εκτελεστεί τέσσερις φορές με κάθε `Identifier` (περιλαμβάνοντας το `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
Αυτές οι εκτελέσεις είναι όλες στο node **enter**. Ωστόσο, υπάρχει επίσης η δυνατότητα κλήση μιας visitor μέθοδο κατά το **exit**.
Φανταστείτε πως έχουμε αυτή τη δομή δέντρου:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
Καθώς διασχίζουμε το κάθε κλαδί του δέντρου από πάνω προς τα κάτω βρίσκουμε εν τέλει ένα τέλος και χρειάζεται να το ξαναδιασχίσουμε προς την άλλη κατεύθυνση για να βρούμε το επόμενο node. Κατεβαίνοντας το δέντρο κάνουμε **enter** το κάθε node και ανεβαίνοντάς το κάνουμε **exit** το κάθε node.
Ας δούμε πως μοιάζει αυτή η διαδικασία για το παραπάνω δέντρο.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
Οπότε δημιουργώντας έναν visitor έχετε δυο ευκαιρίες να επισκεφτείτε ένα node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Καθοδήγηση
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths στους Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Σημείωση: Αυτό δεν είναι αντικατάσταση της λεπτομερής τεκμηρίωση του API που θα είναι διαθέσιμο αλλού σύντομα.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Σημείωση:** Το `sourceType` έχει ως προεπιλογή το `"script"` και θα εμφανίσει σφάλμα όταν βρει `import` ή `export`. Περάστε `sourceType: "module"` για να απαλλαγείτε από αυτά τα σφάλματα.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Ορισμοί
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Διαδικασίες επικύρωσης
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Μετατροπείς
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Γράφοντας το πρώτο σας Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Λειτουργίες μετατροπής
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Επιλογές βυσμάτων (plugins)
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Χτίζοντας Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Βέλτιστες πρακτικές
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Αποφύγετε όσο το δυνατόν την διάσχιση των AST
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Συγχωνεύστε τους επισκέπτες οπότε είναι δυνατό
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Μην εκτελείτε την διαδικασία διάσχισης όταν εκτελείται χειροκίνητη αναζήτηση
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Βελτιστοποίηση των ένθετων επισκέπτών
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Έχοντας επίγνωση των ένθετων δομών
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/el/user-handbook.md
================================================
# Εγχειρίδιο Χρήσης Babel
Αυτό το κέιμενο καλύπτει οτι θα ήθελε να μάθει κάποιος σχετικά με την χρήση του [Babel](https://babeljs.io) και τα σχετικά εργαλεία.
[](http://creativecommons.org/licenses/by/4.0/)
Το εγχειρίδιο αυτό είναι διαθέσιμο και σε άλλες γλώσσες, δείτε στο [README](/README.md) τις υπόλοιπες γλώσσες.
# ΠΙΝΑΚΑΣ ΠΕΡΙΕΧΟΜΕΝΩΝ
* [Εισαγωγή](#toc-introduction)
* [Ρύθμιση του Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Λειτουργία του Babel CLI μέσα από ένα έργο](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Ρυθμίζοντας το Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Εκτέλεση του παραγώμενου κώδικα απο το Babel](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Ρυθμίζοντας το Babel (για προχωρημένους)](#toc-configuring-babel-advanced)
* [Χειροκίνητος καθορισμός βυσμάτων (plugins)](#toc-manually-specifying-plugins)
* [Επιλογές βυσμάτων (plugins)](#toc-plugin-options)
* [Ρυθμίζοντας το Babel βασιζόμενος στο περιβάλλον](#toc-customizing-babel-based-on-environment)
* [Φτιάχνοντας τη δική σου προεπιλογή](#toc-making-your-own-preset)
* [Babel και άλλα εργαλεία](#toc-babel-and-other-tools)
* [Εργαλεία στατικής ανάλυσης](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Στυλ κώδικα](#toc-code-style)
* [Τεκμηρίωση](#toc-documentation)
* [Πλαισια (Frameworks)](#toc-frameworks)
* [React](#toc-react)
* [Κειμενογράφοι και IDEs](#toc-text-editors-and-ides)
* [Υποστήριξη του Babel](#toc-babel-support)
* [Φόρουμ του Babel](#toc-babel-forum)
* [Χώρος Συνομιλίας Babel](#toc-babel-chat)
* [Προβλήματα με το Babel](#toc-babel-issues)
* [Δημιουργούντας μια καταπληκτική αναφορά σφάλματος για το Babel](#toc-creating-an-awesome-babel-bug-report)
# Εισαγωγή
Το Babel είναι ένας γενικός μεταγλωττιστής πολλών χρήσεων για JavaScript. Χρησιμοποιώντας το Babel μπορείς να χρησιμοποιήσεις (και να δημιουργήσεις) την επόμενη γενιά JavaScript καθώς επίσης και την επόμενη γενιά εργαλείων για JavaScript.
Η JavaScript ως γλώσσα εξελίσσεται συνεχώς με νέα χαρακτηριστικά καθώς επισης και προτάσεις χαρακτηριστικών. Χρησιμοποιώντας το Babel θα έχεις την δυνατότητα να χρησιμοποιήσεις πολλά από τα χαρακτηριστικά πριν την καθολική διάθεση τους.
Το Babel το επιτυγχάνει αυτό με την μετάφραση σε κώδικα JavaScript χρησιμοποιώντας τα τελευταία πρότυπα σε μια έκδοση η οποία θα δουλέψει παντού σήμερα. Αυτή η διαδικασία είναι γνωστή ως μεταγλώττιση από πηγαίο κώδικα σε πηγαιό κώδικα, γνωστή και ως transpiling.
Για παράδειγμα, το Babel θα μπορούσε να μετατρέψει την νέα ES2015 συνάρτηση βέλους (arrow function) από αυτή τη μορφή:
```js
const square = n => n * n;
```
στην παρακάτων:
```js
const square = function square(n) {
return n * n;
};
```
Ωστόσω, το Babel μπορεί να κάνει πολλά περισσότερα από αυτό, αφού το Babel υποστηρίζει την σύνταξη επεκτάσεων (extensions) όπως η σύνταξη JSX που χρησιμοποιηεί το React για τον έλεγχο στατικών τύπων.
Πέρα από αυτό, τα πάντα στο Babel είναι απλά ένα plugin και ο καθένας μπορεί να δημιουργήσει τα δικά του plugins χρησιμοποιώντας την πλήρη δύναμη του Babel.
*Περαιτέρω*, το Babel χωρίζεται σε διάφορες ενότητες πυρήνων που ο καθένας μπορεί να χρησιμοποιήσει για να χτίσει την επόμενη γενεά των εργαλείων JavaScript.
Πολλοί άνθρωποι το κάνουν αυτό και το οικοσύστημα που έχει ξεφυτρώσει γύρω από το Babel είναι τεράστιο και πολύ ετερογενές. Στον οδηγό αυτό θα καλύψω πώς τα ενσωματωμένα εργαλεία στο Babel λειτουργούν καθώς και μερικά χρήσιμα πράγματα που προέρχονται από την Κοινότητα.
> ***Για μελλοντικές ενημερώσεις ακολουθήστε τον [@thejameskyle](https://twitter.com/thejameskyle) στο Twitter.***
* * *
# Ρύθμιση του Babel
Δεδομένου ότι η Κοινότητα JavaScript δεν έχει κανένα single build tool, framework, πλατφόρμα, κ.λπ., το Babel έχει επίσημες ενσωματώσεις για όλα τα σημαντικά εργαλεία. Τα πάντα από το Gulp εως το Browserify, από το Ember μέχρι το Meteor, δεν έχει σημασία πως μοιάζει η εγκατάστασή σας καθώς υπάρχει πιθανώς μια επίσημη ένταξη.
Για τους σκοπούς του παρόντος οδηγού, πρόκειται απλώς να καλύψουμε τους ενσωματωμένους τρόπους για τη δημιουργία Babel, αλλά μπορείτε να επισκεφθείτε τη διαδραστική [σελίδα εγκατάστασης](http://babeljs.io/docs/setup) για όλες τις ενσωματώσεις.
> **Σημείωση:** Αυτός ο οδηγός θα αναφέρεται σε εργαλεία γραμμής εντολών όπως `node` και `npm`. Πριν πάτε παρακάτω θα πρέπει να αισθάνεστε άνετα με αυτά τα εργαλεία.
## `babel-cli`
Το CLI του Babel είναι ένας απλός τρόπος για τη μεταγλώττιση των αρχείων με Babel από τη γραμμή εντολών.
Ας το εγκαταστήσουμε αρχικά παγκόσμια στον υπολογιστή μας για να δούμε τα βασικά.
```sh
$ npm install --global babel-cli
```
Μπορούμε να συντάξουμε το πρώτο μας αρχείο ως εξής:
```sh
$ babel my-file.js
```
Αυτό θα αποτυπώσει το μεταγλωττισμένο αποτέλεσμα απευθείας στο τερματικό σας. Για να το γράψουμε σε ένα αρχείο θα πρέπει να καθορίσετε ένα `--out-file` ή `-o`.
```sh
$ babel example.js --out-file compiled.js
# ή
$ babel example.js -o compiled.js
```
Αν θέλουμε να μεταγλωττίσουμε έναν ολόκληρο κατάλογο σε έναν νέο κατάλογο, μπορούμε να το κάνουμε χρησιμοποιώντας `--out-dir` ή `-d`.
```sh
$ babel src --out-dir lib
# ή
$ babel src -d lib
```
### Λειτουργία του Babel CLI μέσα από ένα project
Ενώ *μπορείτε* να εγκαταστήσετε το Babel CLI παγκοσμίως στο μηχάνημά σας, είναι πολύ καλύτερo να το εγκαταστήσετε **σε τοπικό επίπεδο** σε κάθε σας project.
Υπάρχουν δύο βασικοί λόγοι για αυτό.
1. Διαφορετικά projects στην ίδια μηχανή μπορεί να εξαρτώνται από διαφορετικές εκδόσεις του Βαβέλ, επιτρέποντάς σας να ενημερώσετε ένα κάθε φορά.
2. Αυτό σημαίνει ότι δεν έχετε μια έμμεση εξάρτηση από το περιβάλλον σας. Κάνοντας έτσι το project σας πιο φορητό και εύκολο στην εγκατάσταση.
Μπορούμε να εγκαταστήσουμε το Babel CLI τοπικά εκτελώντας:
```sh
$ npm install --save-dev babel-cli
```
> **Σημείωση:** Δεδομένου ότι γενικά είναι κακή ιδέα να εκτελέσετε Βαβέλ σε παγκόσμιο επίπεδο, μπορεί να θέλετε να απεγκαταστήσετε το καθολικό αντίγραφο, εκτελώντας:
>
> ```sh
$ npm uninstall --global babel-cli
```
Αφού ολοκληρωθεί η εγκατάσταση, το αρχείο `package.json` σας πρέπει να μοιάσει με αυτό:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Τώρα, αντί να εκτελούμε το Babel απευθείας από τη γραμμή εντολών θα πάμε να θέσουμε τις εντολές μας σε **npm scripts** που θα χρησιμοποιούν την τοπική μας έκδοση.
Απλά προσθέστε ένα πεδίο `"scripts"` στο `package.json` και βάλτε την εντολή Babel στο εσωτερικό ως `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Τώρα από τον τερματικό μπορούμε να τρέξουμε:
```js
npm run build
```
Αυτό θα εκτελέσει το Babel με τον ίδιο τρόπο όπως πριν, μόνο που τώρα χρησιμοποιούμε ένα τοπικό αντίγραφο.
## `babel-register`
Η επόμενη πιο κοινή μέθοδος λειτουργίας του Babel είναι μέσω του `babel-register`. Αυτή η επιλογή θα σας επιτρέψει να εκτελέσετε το Babel μόνο απαιτώντας αρχεία, το οποίο μπορεί να ενσωματωθεί καλύτερα με τις ρυθμίσεις σας.
Σημειώστε ότι αυτό δεν προορίζεται για χρήση παραγωγής. Θεωρείται κακή πρακτική η ανάπτυξη κώδικα που μεταφράζεται με τον τρόπο αυτό. Είναι πολύ καλύτερο να μεταγλωττίσετε πριν από την ανάπτυξη. Ωστόσο, αυτό λειτουργεί αρκετά καλά για build scripts ή άλλα πράγματα που μπορείτε να εκτελέσετε σε τοπικό επίπεδο.
Ας δημιουργήσουμε αρχικά ένα `index.js` αρχείο στο project μας.
```js
console.log("Hello world!");
```
Εάν επρόκειτο να τρέξουμε αυτό με `node index.js` δεν θα να μεταγλωττιζόταν με Babel. Έτσι, αντί να κάνουμε αυτό, θα στήσουμε `babel-register`.
Πρώτα να εγκαταστήσετε `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Στη συνέχεια, δημιουργήστε ένα αρχείο `register.js` μέσα στο project και γράψτε τον ακόλουθο κώδικα:
```js
require("babel-register");
require("./index.js");
```
Αυτό που κάνει είναι *registers* το Babel στο σύστημα ενότητας του Node και αρχίζει την μεταγλώτισση κάθε αρχείου που είναι `require`'d.
Τώρα, αντί να εκτελούμε `node index.js` μπορούμε να χρησιμοποιήσουμε `register.js`.
```sh
$ node register.js
```
> **Σημείωση:** Δεν μπορείτε να καταχωρήσετε Βαβέλ στο ίδιο αρχείο που θέλετε να μεταγλωττίσετε. Αυτό συμβαίνει επειδή το Node εκτελεί το αρχείο πριν το Babel έχει την ευκαιρία να το μεταγλωττίσεi.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
Εάν απλά εκτελείτε κώδικα μέσω του `node` CLI ο ευκολότερος τρόπος να ενσωματωθεί το Babel ίσως είναι να χρησιμοποιηθεί το `babel-node` CLI που σε μεγάλο βαθμό είναι αντικαταστάτης για το `node` CLI.
Σημειώστε ότι αυτό δεν προορίζεται για χρήση παραγωγής. Θεωρείται κακή πρακτική η ανάπτυξη κώδικα που μεταφράζεται με τον τρόπο αυτό. Είναι πολύ καλύτερο να μεταγλωττίσετε πριν από την ανάπτυξη. Ωστόσο, αυτό λειτουργεί αρκετά καλά για build scripts ή άλλα πράγματα που μπορείτε να εκτελέσετε σε τοπικό επίπεδο.
Πρώτα, βεβαιωθείτε ότι έχετε το `babel-cli` εγκατεστημένο.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Στη συνέχεια, αντικαταστήστε όπου εκτελείτε `node` με `babel-node`.
Εάν χρησιμοποιείτε npm `scripts`, μπορείτε απλά να κάνετε:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Διαφορετικά θα πρέπει να καταγράψετε τη διαδρομή στο `babel-node`.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Συμβουλή: Μπορείτε επίσης να χρησιμοποιήσετε [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
Εάν χρειάζεστε να χρησιμοποιήσετε Βαβέλ προγραμματιστικά για κάποιο λόγο, μπορείτε να χρησιμοποιήσετε το ίδιο το πακέτο `babel-core`.
Πρώτα να εγκαταστήσετε `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
Αν έχετε μια σειρά από JavaScript μπορείτε να τη μεταγλωττίσετε απευθείας χρησιμοποιώντας `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
Εάν εργάζεστε με αρχεία, μπορείτε να χρησιμοποιήσετε είτε το ασύγχρονο api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Ή το σύγχρονο api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
Αν έχετε ήδη μια Babel AST για οποιοδήποτε λόγο μπορείτε να τη μετατρέψετε από την AST άμεσα.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Ρυθμίζοντας το Babel
Μπορεί να έχετε παρατηρήσει από τώρα ότι τρέχοντας Babel από μόνο του δεν φαίνεται να κάνει οτιδήποτε άλλο εκτός από να αντιγράφει JavaScript αρχεία από μία θέση σε άλλη.
Αυτό είναι επειδή δεν το έχουμε πει στο Babel να κάνει τίποτα ακόμα.
> Δεδομένου ότι το Babel είναι γενικής χρήσης μεταγλωττιστής που χρησιμοποιείται σε πληθώρα διαφορετικών τρόπων, δεν κάνει τίποτα από προεπιλογή. Πρέπει να πείτε στο Babel τι έχει να κάνει.
Μπορείτε να δώσετε οδηγίες Βαβέλ για το τι να κάνει με την εγκατάσταση **plugins** ή **presets** (Γκρουπ plugins).
## `.babelrc`
Πριν ξεκινήσουμε να λέμε στο Babel τι να κάνει πρέπει να δημιουργήσουμε ένα αρχείο ρύθμισης παραμέτρων. Το μόνο που χρειάζεται να κάνετε είναι να δημιουργήσετε ένα αρχείο `.babelrc` στη ρίζα του έργου σας. Ξεκινήστε ως εξής:
```js
{
"presets": [],
"plugins": []
}
```
Το αρχείο αυτό είναι πώς ρυθμίζετε το Babel για να το κάνεi αυτό που θέλετε.
> **Σημείωση:** Ενώ μπορείτε να περάσετε επίσης επιλογές στο Babel με άλλους τρόπους, το αρχείο `.babelrc` είναι σύμβαση και είναι ο καλύτερος τρόπος.
## `babel-preset-es2015`
Ας ξεκινήσουμε λέγοντας στο Babel να μεταγλωττίζει ES2015 (την τελευταία έκδοση του προτύπου JavaScript, επίσης γνωστή ως ES6) σε ES5 (διαθέσιμο στα περισσότερα περιβάλλοντα JavaScript σήμερα).
Θα κάνουμε αυτό εγκαθιστώντας τις "es2015" Babel προκαθορισμένες:
```sh
$ npm install --save-dev babel-preset-es2015
```
Στη συνέχεια θα τροποποιήσουμε το `.babelrc` για να συμπεριλάβει την προκαθορισμένη.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Η ρύθμιση React είναι εξίσου εύκολη. Απλά εγκαταστήστε την προκαθορισμένη:
```sh
$ npm install --save-dev babel-preset-react
```
Στη συνέχεια, προσθέστε την προκαθορισμένη στο αρχείο `.babelrc`:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
Η JavaScript έχει επίσης ορισμένες προτάσεις μέσα από την TC39 (η τεχνική επιτροπή πίσω από το ECMAScript πρότυπο) διαδικασία.
Έχουμε σπάσει αυτήν την διαδικασία σε 5 στάδια (0-4). Αφού οι προτάσεις κερδίζουν περισσότερη έλξη και είναι πιο πιθανό να γίνουν δεκτές στο standard, προχωρούν στα διάφορα στάδια, ώσπου τελικά γίνονται δεκτές στο στάδιο 4.
Αυτές ομαδοποιούνται στο Babel ως 4 διαφορετικές προεπιλογές:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Σημειώστε ότι δεν υπάρχει κανένα στάδιο-4, δεδομένου ότι είναι απλά οι `es2015` προκαθορισμένες παραπάνω.
Κάθε μία από αυτές τις προεπιλογές απαιτεί την προκαθορισμένη ρύθμιση για τα προχωρημένα στάδια. δηλαδή η `babel-preset-stage-1` απαιτεί `babel-preset-stage-2` που απαιτεί `babel-preset-stage-3`.
Απλά εγκαταστήστε το στάδιο το οποίο ενδιαφέρεστε να χρησιμοποιήσετε:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Στη συνέχεια, μπορείτε να το προσθέσετε στο αρχείο config `.babelrc`.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Εκτέλεση του παραγώμενου κώδικα απο το Babel
Έτσι έχετε μεταγλωττίσει τον κωδικό σας με Babel, αλλά αυτό δεν είναι το τέλος της ιστορίας.
## `babel-polyfill`
Σχεδόν όλη η φουτουριστική JavaScript σύνταξη μπορεί να μεταγλωττιστεί με Babel, αλλά το ίδιο δεν ισχύει για τα API.
Για παράδειγμα, ο ακόλουθος κώδικας έχει μια arrow λειτουργία που πρέπει να μεταγλωττιστεί:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Που μετατρέπεται σε αυτό:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
Ωστόσο, αυτό δεν θα λειτουργεί ακόμα παντού επειδή δεν υπάρχει `Array.from` σε κάθε περιβάλλον JavaScript.
Uncaught TypeError: Array.from is not a function
Για να λύσουμε αυτό το πρόβλημα μπορούμε να χρησιμοποιήσουμε κάτι που ονομάζεται [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Με απλά λόγια, ένα polyfill είναι ένα κομμάτι του κώδικα που αναπαράγει ένα native api που δεν υπάρχει στο τρέχον χρόνο εκτέλεσης. Επιτρέποντάς σας να χρησιμοποιήσετε API, όπως `Array.from` πριν να είναι διαθέσιμα.
Το Babel χρησιμοποιεί το εξαιρετικό [core-js](https://github.com/zloirock/core-js) ως polyfill του, μαζί με ένα προσαρμοσμένο [regenerator](https://github.com/facebook/regenerator) για να κάνει γεννήτριες και ασύγχρονες λειτουργίες εργασίας να δουλεύουν.
Για να συμπεριλάβετε το Babel polyfill, πρώτα εγκαταστήστε το με npm:
```sh
$ npm install --save babel-polyfill
```
Στη συνέχεια, απλώς συμπεριλάβετε το polyfill στην κορυφή του κάθε αρχείου που το χρειάζεται:
```js
import "babel-polyfill";
```
## `babel-runtime`
Προκειμένου να υλοποιηθούν λεπτομέρειες σχετικά με ECMAScript specs, το Babel θα χρησιμοποιήσει «βοηθητικές» μεθόδους για να κρατήσει καθαρό κώδικα που δημιουργείται.
Δεδομένου ότι αυτές οι βοηθητικές μέθοδοι μπορούν να γίνουν αρκετά εκτενή, και προστίθενται στην κορυφή του κάθε αρχείου, μπορείτε να τα μετακινήσετε σε ένα ενιαίο «χρόνο εκτέλεσης» που παίρνει την απαιτούμενη.
Ξεκινήστε εγκαθιστώντας `babel-plugin-transform-runtime` και `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Στη συνέχεια να ενημερώσετε το `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Τώρα το Babel θα μεταγλωττίσει κώδικα ως εξής:
```js
class Foo {
method() {}
}
```
Σε αυτό:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Αντί να βάλουμε τους `_classCallCheck` και `_createClass` βοηθούς σε κάθε ενιαίο αρχείο εκεί που χρειάζονται.
* * *
# Ρυθμίζοντας το Babel (για προχωρημένους)
Οι περισσότεροι άνθρωποι μπορούν να κάνουν τη δουλειά τους χρησιμοποιώντας το Babel μόνο με τις ενσωματωμένες προεπιλογές, αλλά το Babel μπορεί να παρέχει πολλή περισσότερη δύναμη από αυτό.
## Χειροκίνητος καθορισμός βυσμάτων (plugins)
Οι προεπιλογές του Babel είναι απλά συλλογές των προ-ρυθμισμένων plugins, αν θέλετε να κάνετε κάτι διαφορετικό μπορείτε να καθορίσετε οι ίδιοι τα plugins. Αυτό λειτουργεί σχεδόν ακριβώς με τον ίδιο τρόπο όπως οι προκαθορισμένες ρυθμίσεις.
Πρώτα να εγκαταστήσετε ένα plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Στη συνέχεια, προσθέστε το πεδίο `plugins` στο `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
Αυτό σας δίνει πολύ λεπτομερές έλεγχο πάνω στους μετασχηματισμούς που εκτελείτε.
Για να δείτε μια πλήρη λίστα των επίσημων plugins ανατρέξτε στη [σελίδα Babel Plugins](http://babeljs.io/docs/plugins/).
Επίσης ρίξτε μια ματιά σε όλα τα plugins που έχουν [κατασκευαστεί από την Κοινότητα](https://www.npmjs.com/search?q=babel-plugin). Αν θα θέλατε να μάθετε πώς να γράψετε τα δικά σας plugins διαβάστε το [Εγχειρίδιο Babel Plugin](plugin-handbook.md).
## Επιλογές βυσμάτων (plugins)
Πολλά plugins, επίσης, έχουν επιλογές ρύθμισης των παραμέτρων τους ώστε να συμπεριφέρονται διαφορετικά. Για παράδειγμα, πολλοί μετασχηματισμοί έχουν ένα «χαλαρό» mode, το οποίο δεν περιέχει κάποια spec, συμπεριφορά που ευνοεί απλούστερο και περισσότερο αποδοτικό κώδικα που δημιουργείται.
Για να προσθέσετε επιλογές σε ένα plugin, απλά κάνετε τις παρακάτω αλλαγές:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> Θα εργάζομαι σε ενημερωμένες εκδόσεις για τα plugins ώστε να αποδώσω κάθε λεπτομέρεια στις επιλογές που παρέχουν τις επόμενες εβδομάδες. [Ακολουθήστε με για ενημερωμένες εκδόσεις](https://twitter.com/thejameskyle).
## Ρυθμίζοντας το Babel βασιζόμενος στο περιβάλλον
Τα Babel plugins δίνουν λύσεις σε πολλές διαφορετικές εργασίες. Πολλά από αυτά είναι εργαλεία ανάπτυξης που μπορούν να σας βοηθήσουν για τον εντοπισμό σφαλμάτων στον κώδικά σας ή να ενσωματώσετε με εργαλεία. Υπάρχουν επίσης πολλά plugins που προορίζονται για τη βελτιστοποίηση τον κωδικό σας στην παραγωγή.
Για το λόγο αυτό, είναι κοινό να θέλετε ρύθμισης παραμέτρων Babel με βάση το περιβάλλον. Μπορείτε να κάνετε αυτό εύκολα με το αρχείο `.babelrc`.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Το Babel θα επιτρέψει τη διαμόρφωση στο εσωτερικό `env` με βάση το τρέχον περιβάλλον.
Στο συγκεκριμένο περιβάλλον θα χρησιμοποιήσει `process.env.BABEL_ENV`. Όταν το `BABEL_ENV` δεν είναι διαθέσιμο, θα επιστρέψει στο `NODE_ENV`, και αν αυτό δεν είναι διαθέσιμο θα πάει στην προεπιλογή `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Σημείωση:** `[COMMAND]` είναι ό, τι χρησιμοποιείτε για να εκτελέσετε το Babel (πχ. `babel`, `babel-node`, ή ίσως απλά `node` εάν χρησιμοποιείτε το register hook).
>
> **Συμβουλή:** Αν θέλετε οι εντολές σας να λειτουργούν σε όλες τις πλατφόρμες unix και windows χρησιμοποιήστε [`cross-env`](https://www.npmjs.com/package/cross-env).
## Φτιάχνοντας τη δική σου προεπιλογή
Χειροκίνητος καθορισμός plugins; Επιλογές plugin; Ρυθμίσεις βασισμένες στο περιβάλλον; Όλη αυτή η ρύθμιση παραμέτρων μπορεί να φαίνεται σαν να δίνει έναν τόνο της επανάληψης σε όλα τα projects σας.
Για το λόγο αυτό, ενθαρρύνουμε την Κοινότητα να δημιουργεί τις δικές της προεπιλογές. Αυτό θα μπορούσε να είναι μια προκαθορισμένη ρύθμιση για τo συγκεκριμένo [node version](https://github.com/leebenson/babel-preset-node5) που εκτελείτε, ή ίσως μια προκαθορισμένη ρύθμιση για [ολόκληρη](https://github.com/cloudflare/babel-preset-cf) την [εταιρεία](https://github.com/airbnb/babel-preset-airbnb).
Είναι εύκολο να δημιουργήσετε μια προκαθορισμένη ρύθμιση. Ας πούμε ότι έχετε αυτό το αρχείο `.babelrc`:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
Το μόνο που χρειάζεται να κάνετε είναι να δημιουργήστε ένα νέο project με τη σύμβαση ονομασίας `babel-preset-*` (παρακαλώ να είστε υπεύθυνοι με αυτόν το χώρο ονομάτων), και να δημιουργήσετε δύο αρχεία.
Πρώτα, μπορείτε να δημιουργήσετε ένα νέο αρχείο `package.json` με τις απαραίτητες `dependencies` για την προκαθορισμένη.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Στη συνέχεια, δημιουργήστε ένα αρχείο `index.js` που εξάγει το περιεχόμενο του αρχείου `.babelrc`, αντικαθιστώντας το plugin/preset strings με `require` κλήσεις.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Στη συνέχεια απλά το δημοσιεύετε στο npm και μπορείτε να το χρησιμοποιήσετε όπως θα κάνατε με οποιοδήποτε προκαθορισμένη.
* * *
# Babel και άλλα εργαλεία
Το Babel είναι αρκετά απλό να στήσετε μόλις πάρετε μια ιδέα, αλλά μπορεί να είναι δύσκολη η πλοήγηση στο πώς να το ρυθμίσετε με άλλα εργαλεία. Ωστόσο, προσπαθούμε να συνεργαζόμαστε στενά με άλλα έργα, προκειμένου η εμπειρία να είναι όσο πιο εύκολη γίνεται.
## Εργαλεία στατικής ανάλυσης
Νεώτερα πρότυπα φέρνουν νέα σύνταξη της γλώσσας και τα εργαλεία στατικής ανάλυσης μόλις τώρα αρχίζουν να επωφελούνται από αυτό.
### Linting
Ένα από τα πιο δημοφιλή εργαλεία για linting είναι [ESLint](http://eslint.org), για το λόγο αυτό διατηρούμε μια επίσημη [`babel-eslint`](https://github.com/babel/babel-eslint) ολοκλήρωση.
Πρώτα να εγκαταστήσετε `eslint` και `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Ύστερα να δημιουργήσετε ή να χρησιμοποιήσετε το υπάρχον αρχείο `.eslintrc` στο project σας και να ορίσετε το `parser` ως `babel-eslint` .
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Τώρα μπορείτε να προσθέσετε μια εργασία `lint` στα npm `package.json` σενάρια:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Στη συνέχεια, απλά εκτελέστε την εργασία και θα είναι όλα έτοιμα.
```sh
$ npm run lint
```
Για περισσότερες πληροφορίες συμβουλευτείτε την τεκμηρίωση του [`babel-eslint`](https://github.com/babel/babel-eslint) ή [`eslint`](http://eslint.org).
### Στυλ κώδικα
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
Το JSCS είναι ένα εξαιρετικά δημοφιλές εργαλείο για να πάτε το linting ένα βήμα παραπέρα στον έλεγχο του στυλ του ίδιου του κώδικα. Ένας κύριος συντηρητής του Babel και των JSCS projects ([@hzoo](https://github.com/hzoo)) διατηρεί την επίσημη ενσωμάτωση με JSCS.
Ακόμα καλύτερα, αυτή η ολοκλήρωση τώρα ζει μέσα στο JSCS το ίδιο υπό την `--esnext` επιλογή. Έτσι η ενσωμάτωση του Babel γίνεται τόσο απλά:
$ jscs . --esnext
Από το cli, ή προσθέτοντας την επιλογή `esnext` στο αρχείο `.jscsrc`.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
Για περισσότερες πληροφορίες συμβουλευτείτε την τεκμηρίωση του [`babel-eslint`](https://github.com/babel/babel-eslint) ή [`eslint`](http://eslint.org).
### Τεκμηρίωση
Χρησιμοποιώντας Babel, ES2015 και Flow μπορείτε να συμπεράνετε πολλά σχετικά με τον κώδικά σας. Χρησιμοποιώντας το [documentation.js](http://documentation.js.org) μπορείτε να δημιουργήσετε πολύ εύκολα αναλυτική τεκμηρίωση API.
Το documentation.js χρησιμοποιεί το Babel στο παρασκήνιο για την υποστήριξη όλων των την σύνταξεων συμπεριλαμβανομένων Flow annotations για τη δήλωση των τύπων στον κώδικά σας.
## Πλαισια (Frameworks)
Όλα τα μεγάλα JavaScript frameworks εστιάζουν τώρα στην ευθυγράμμιση των APIs γύρω από το μέλλον της γλώσσας. Για το λόγο αυτό, έχει υπάρξει πολλή δουλειά πάνω στο tooling.
Τα φrameworks έχουν την ευκαιρία όχι μόνο να χρησιμοποιούν Babel αλλά να το επεκτείνουν με τρόπους που να βελτιώνουν την εμπειρία των χρηστών τους.
### React
Το React έχει αλλάξει δραματικά τα API του για να ευθυγραμμιστεί με τις ES2015 τάξεις ([Διαβάστε για το ενημερωμένο API εδώ](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Ακόμη περισσότερο, το React βασίζεται στο Babel για τη μεταγλώττιση του JSX συντακτικού, αποδοκιμάζοντας τα δικά του εργαλεία σε σχέση με το Babel. Μπορείτε να ξεκινήσετε με τη δημιουργία του πακέτου `babel-preset-react` ακολουθώντας τις [οδηγίες παραπάνω](#babel-preset-react).
Η κοινότητα του React πήρε το Babel και έφτιαξε μια σειρά μετασχηματισμών που μπορείτε να [διαβάσετε εδώ](https://www.npmjs.com/search?q=babel-plugin+react).
Κυρίως το plugin [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) που σε συνδυασμό με μια σειρά από [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) μπορεί να ενεργοποιεί πράγματα όπως *hot module reloading* και άλλα βοηθητικά προγράμματα εντοπισμού σφαλμάτων.
## Κειμενογράφοι και IDEs
Η εισαγωγή ES2015, JSX και σύνταξη Flow με Babel μπορεί να είναι χρήσιμη, αλλά αν δεν το υποστηρίζει το πρόγραμμα επεξεργασίας κειμένου μπορεί να είναι μια πραγματικά κακή εμπειρία. Για το λόγο αυτό, θα θέλετε να στήσετε τα πρόγραμμα επεξεργασίας κειμένου ή IDE με ένα Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Υποστήριξη του Babel
Το Babel έχει μια πολύ μεγάλη και ταχέως αυξανόμενη κοινότητα, όπως μεγαλώνουμε θέλουμε να εξασφαλίσουμε ότι οι άνθρωποι έχουν όλους τους πόρους που χρειάζονται για να είναι επιτυχής. Για αυτό παρέχουμε έναν αριθμό διαφορετικών καναλιών για να έχετε υποστήριξη.
Να θυμάστε ότι σε όλες αυτές τις κοινότητες μπορούμε να επιβάλουμε έναν [Κώδικα δεοντολογίας](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). Αν σπάσει ο κώδικας δεοντολογίας, θα ληφθούν μέτρα. Έτσι παρακαλούμε να τον διαβάσετε και να τον θυμάστε κατά την αλληλεπίδραση με άλλους.
Θέλουμε επίσης να χτίσουμε μια self-supporting κοινότητα, για τους ανθρώπους που υπάρχουν καιρό και βοηθάνε τα άλλα μέλη. Αν βρείτε κάποιον να ρωτάει κάτι το οποίο ξέρετε να απαντήσετε, πάρτε μερικά λεπτά και βοηθήστε τον. Προσπαθήστε να είστε όσο πιο ευγενικοί γίνεται.
## Φόρουμ του Babel
Το [Discourse](http://www.discourse.org) μας έχει εφοδιάσει με ηλεκτρονική έκδοση του λογισμικού τους φόρουμ δωρεάν (και τους αγαπάμε για αυτό!). Αν σας αρέσουν τα φόρουμ παρακαλώ ρίξτε μια ματιά στο [discuss.babeljs.io](https://discuss.babeljs.io).
## Χώρος Συνομιλίας Babel
Όλοι αγαπούν [Slack](https://slack.com). Αν ψάχνετε για άμεση υποστήριξη από την κοινότητα τότε επικοινωνήστε μαζί μας στο [slack.babeljs.io](https://slack.babeljs.io).
## Προβλήματα με το Babel
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
Εάν θέλετε να ανοίξετε ένα νέο θέμα:
* [Αναζήτηση για ένα υπάρχον θέμα](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Δημιουργούντας μια καταπληκτική αναφορά σφάλματος για το Babel
Τα Babel θέματα μπορεί να είναι μερικές φορές πολύ δύσκολο να εντοπιστούν εξ αποστάσεως, έτσι χρειαζόμαστε όλη τη βοήθεια που μπορούμε να πάρουμε. Δαπανώντας μερικά παραπάνω λεπτά γράφοντας μια καλή αναφορά θέματος μπορεί να βοηθήσει να βρεθεί λύση στο πρόβλημά σας αισθητά πιο γρήγορα.
Πρώτα, δοκιμάστε απομονώνοντας το πρόβλημά σας. Είναι εξαιρετικά απίθανο ότι κάθε τμήμα της εγκατάστασης σας συμβάλλει στο πρόβλημα. Εάν το πρόβλημά σας είναι ένα κομμάτι του κώδικα εισόδου, προσπαθήστε να διαγράψετε όσο κώδικα όσο το δυνατόν που προκαλεί ακόμα ένα θέμα.
> [WIP]
* * *
> ***Για μελλοντικές ενημερώσεις ακολουθήστε τον [@thejameskyle](https://twitter.com/thejameskyle) στο Twitter.***
================================================
FILE: translations/en/README.md
================================================
# Babel Handbook
This handbook is divided into two parts:
- [User Handbook](user-handbook.md) - How to setup/configure Babel and more.
- [Plugin Handbook](plugin-handbook.md) - How to create plugins for Babel.
If you are reading a non-english translation of this handbook you may still find
english sections that have not yet been translated. If you would like to
contribute to one of the translations you must do so through Crowdin. Please
read the [contributing guidelines](/CONTRIBUTING.md) for more information. You
will find a number of english words that are programming concepts. If these were
translated to other languages there would be a lack of consistency and fluency
when reading about them. In many cases you will find the literal translation
followed by the english term in parenthesis `()`. For example: Abstract Syntax
Trees (ASTs).
================================================
FILE: translations/en/plugin-handbook.md
================================================
# Babel Plugin Handbook
Written by [Jamie Kyle](https://jamie.build/)
This document covers how to create [Babel](https://babeljs.io)
[plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for
a complete list.
# Table of Contents
- [Introduction](#toc-introduction)
- [Basics](#toc-basics)
- [ASTs](#toc-asts)
- [Stages of Babel](#toc-stages-of-babel)
- [Parse](#toc-parse)
- [Lexical Analysis](#toc-lexical-analysis)
- [Syntactic Analysis](#toc-syntactic-analysis)
- [Transform](#toc-transform)
- [Generate](#toc-generate)
- [Traversal](#toc-traversal)
- [Visitors](#toc-visitors)
- [Paths](#toc-paths)
- [Paths in Visitors](#toc-paths-in-visitors)
- [State](#toc-state)
- [Scopes](#toc-scopes)
- [Bindings](#toc-bindings)
- [API](#toc-api)
- [babel-parser](#toc-babel-parser)
- [babel-traverse](#toc-babel-traverse)
- [babel-types](#toc-babel-types)
- [Definitions](#toc-definitions)
- [Builders](#toc-builders)
- [Validators](#toc-validators)
- [Converters](#toc-converters)
- [babel-generator](#toc-babel-generator)
- [babel-template](#toc-babel-template)
- [Writing your first Babel Plugin](#toc-writing-your-first-babel-plugin)
- [Transformation Operations](#toc-transformation-operations)
- [Visiting](#toc-visiting)
- [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
- [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
- [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
- [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
- [Find a specific parent path](#toc-find-a-specific-parent-path)
- [Get Sibling Paths](#toc-get-sibling-paths)
- [Stopping Traversal](#toc-stopping-traversal)
- [Manipulation](#toc-manipulation)
- [Replacing a node](#toc-replacing-a-node)
- [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
- [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
- [Inserting a sibling node](#toc-inserting-a-sibling-node)
- [Inserting into a container](#toc-inserting-into-a-container)
- [Removing a node](#toc-removing-a-node)
- [Replacing a parent](#toc-replacing-a-parent)
- [Removing a parent](#toc-removing-a-parent)
- [Scope](#toc-scope)
- [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
- [Generating a UID](#toc-generating-a-uid)
- [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
- [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
- [Plugin Options](#toc-plugin-options)
- [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
- [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
- [Building Nodes](#toc-building-nodes)
- [Best Practices](#toc-best-practices)
- [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
- [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
- [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
- [Optimizing nested visitors](#toc-optimizing-nested-visitors)
- [Being aware of nested structures](#toc-being-aware-of-nested-structures)
- [Unit Testing](#toc-unit-testing)
# Introduction
Babel is a generic multi-purpose compiler for JavaScript. More than that it is a
collection of modules that can be used for many different forms of static
analysis.
> Static analysis is the process of analyzing code without executing it.
> (Analysis of code while executing it is known as dynamic analysis). The
> purpose of static analysis varies greatly. It can be used for linting,
> compiling, code highlighting, code transformation, optimization, minification,
> and much more.
You can use Babel to build many different types of tools that can help you be
more productive and write better programs.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle)
> on Twitter.***
----
# Basics
Babel is a JavaScript compiler, specifically a source-to-source compiler,
often called a "transpiler". This means that you give Babel some JavaScript
code, Babel modifies the code, and generates the new code back out.
## ASTs
Each of these steps involve creating or working with an
[Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or
AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babel/blob/master/packages/babel-parser/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node,
or hundreds if not thousands of Nodes. Together they are able to describe the
syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (e.g.
`"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of
Node defines an additional set of properties that describe that particular node
type.
There are additional properties on every Node that Babel generates which
describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Stages of Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Parse
The **parse** stage, takes code and outputs an AST. There are two phases of
parsing in Babel:
[**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and
[**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexical Analysis will take a string of code and turn it into a stream of
**tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntactic Analysis
Syntactic Analysis will take a stream of tokens and turn it into an AST
representation. Using the information in the tokens, this phase will reformat
them as an AST which represents the structure of the code in a way that makes it
easier to work with.
### Transform
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage
takes an AST and traverses through it, adding, updating, and removing nodes as it
goes along. This is by far the most complex part of Babel or any compiler. This
is where plugins operate and so it will be the subject of most of this handbook.
So we won't dive too deep right now.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler))
stage takes the final AST and turns it back into a string of code, also creating
[source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first,
building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to
[traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`,
`params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so
we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child
node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In
this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an
array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to
the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator
isn't a node, just a value, so we don't go to it, and instead just visit `left`
and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them.
The reason we use that term is because there is this concept of a
[**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they
are an object with methods defined for accepting particular node types in a
tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for
> `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the
`Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each
`Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of
calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we
need to traverse back up the tree to get to the next node. Going down the tree
we **enter** each node, then going back up we **exit** each node.
Let's _walk_ through what this process looks like for the above tree.
- Enter `FunctionDeclaration`
- Enter `Identifier (id)`
- Hit dead end
- Exit `Identifier (id)`
- Enter `Identifier (params[0])`
- Hit dead end
- Exit `Identifier (params[0])`
- Enter `BlockStatement (body)`
- Enter `ReturnStatement (body)`
- Enter `BinaryExpression (argument)`
- Enter `Identifier (left)`
- Hit dead end
- Exit `Identifier (left)`
- Enter `Identifier (right)`
- Hit dead end
- Exit `Identifier (right)`
- Exit `BinaryExpression (argument)`
- Exit `ReturnStatement (body)`
- Exit `BlockStatement (body)`
- Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We
could have one giant mutable object that you manipulate and have full access to,
or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and
removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the
tree and all sorts of information about the node. Whenever you call a method
that modifies the tree, this information is updated. Babel manages all of this
for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually
visiting the path instead of the node. This way you are mostly working with the
reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over
again and your assumptions about state will almost always be proven wrong by
some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher
Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate
global state from your visitors.
### Scopes
Next let's introduce the concept of a
[**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript
has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping),
which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable,
function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying
it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we
don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing
ones. Or maybe we just want to find where a variable is referenced. We want to
be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope.
Then during the traversal process it collects all the references ("bindings")
within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get
into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a
**binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what
type of binding it is (parameter, declaration, etc.), lookup what scope it
belongs to, or get a copy of its identifier. You can even tell if it's
constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the
largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
----
# API
Babel is actually a collection of modules. In this section we'll walk through
the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation, which is available [here](https://babeljs.io/docs/usage/api/).
## [`babel-parser`](https://github.com/babel/babel/tree/master/packages/babel-parser)
Started as a fork of Acorn, the Babel Parser is fast, simple to use,
has plugin-based architecture for non-standard features (as well as future
standards).
First, let's install it.
```sh
$ npm install --save @babel/parser
```
Let's start by simply parsing a string of code:
```js
import parser from "@babel/parser";
const code = `function square(n) {
return n * n;
}`;
parser.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
parser.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that
the Babel Parser should parse in. `"module"` will parse in strict mode and allow module
declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds
> `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since the Babel Parser is built with a plugin-based architecture, there is also a
`plugins` option which will enable the internal plugins. Note that the Babel Parser has
not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babel parser docs](https://babeljs.io/docs/en/babel-parser#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible
for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save @babel/traverse
```
We can use it alongside to traverse and update nodes:
```js
import parser from "@babel/parser";
import traverse from "@babel/traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = parser.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains
methods for building, validating, and converting AST nodes. It's useful for
cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save @babel/types
```
Then start using it:
```js
import traverse from "@babel/traverse";
import * as t from "@babel/types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on
what properties belong where, what values are valid, how to build that node, how
the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a
`builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like
this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive
errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields`
of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is
`isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also
pass a second parameter to ensure that the node contains certain properties and
values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, _ehem_, assertive version of these methods, which will
throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it
into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save @babel/generator
```
Then use it
```js
import parser from "@babel/parser";
import generate from "@babel/generator";
const code = `function square(n) {
return n * n;
}`;
const ast = parser.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to
write strings of code with placeholders that you can use instead of manually
building up a massive AST. In computer science, this capability is called
quasiquotes.
```sh
$ npm install --save @babel/template
```
```js
import template from "@babel/template";
import generate from "@babel/generator";
import * as t from "@babel/types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Writing your first Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together
with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types`
like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor
for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===`
operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
----
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
You can't current use `get` on a Container (the `body` array of a `BlockStatement`), but you chain the dot syntax instead.
```js
export default function f() {
return bar;
}
```
For the example above, if you wanted to get the path corresponding to the `return`, you could chain the various properties, using a number as the index when traversing the array.
```js
ExportDefaultDeclaration(path) {
path.get("declaration.body.body.0");
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents.
When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
- Check if a path is part of a list with `path.inList`
- You can get the surrounding siblings with `path.getSibling(index)`,
- The current path's index in the container with `path.key`,
- The path's container (an array of all sibling nodes) with `path.container`
- Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
path.getPrevSibling() // path(undefined) *
path.getNextSibling() // pathB
path.getAllPrevSiblings() // []
path.getAllNextSiblings() // [pathB, pathC]
}
}
};
}
```
* `path(undefined)` is a `NodePath` where the `path.node === undefined`
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path.
`path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be
> statements. This is because Babel uses heuristics extensively when replacing
> nodes which means that you can do some pretty crazy transformations that would
> be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with
> dynamic source strings, otherwise it's more efficient to parse the code
> outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This
> uses the same heuristics mentioned in
> [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into an AST node that is an array like `body`.
Similar to `insertBefore`/`insertAfter`, except that you have to specify the `listKey`, which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined
variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
----
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin
you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other
plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins.
They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Babel plugins themselves can enable [parser plugins](https://babeljs.io/docs/en/babel-parser#plugins) so that users don't need to
install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
```
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
```
----
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert
into the AST. As mentioned previously, you can do this using the
[builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to
build except with the first letter lowercased. For example if you wanted to
build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some
work that's being done to generate easy-to-read documentation on the
definitions, but for now they can all be found
[here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including
how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be
needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the
> node than the `builder` array contains. This is to keep the builder from
> having too many arguments. In these cases you need to set the properties
> manually. An example of this is
> [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs
to be an `Expression` or an `Identifier` depending on if the member expression
is `computed` or not and `computed` is simply a boolean that defaults to
`false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is
`Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an
`aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the
`object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for
every node type. So you should take some time and understand how they are
generated from the node definitions.
You can find all of the actual
[definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)
and you can see them
[documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
----
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions
as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST
more than necessary. This could be thousands if not tens of thousands of
extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in
order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple
places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run
once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular
node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good
chance you can manually lookup the nodes you need without performing a costly
traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your
code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is
called. That can be costly, because Babel does some processing each time a new
visitor object is passed in (such as exploding keys containing multiple types,
performing validation, and adjusting the object structure). Because Babel stores
flags on visitor objects indicating that it's already performed that processing,
it's better to store the visitor in a variable and pass the same object each
time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on
`this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given
structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the
`Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal
above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests.
We'll use [jest](http://facebook.github.io/jest/) for this example because it supports
snapshot testing out of the box. The example we're creating here is hosted in
[this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and
then we can begin writing our first test: the snapshot. Snapshot tests allow us
to visually inspect the output of our babel plugin. We give it an input, tell it to make
a snapshot, and it saves it to a file. We check in the snapshots into git. This allows
us to see when we've affected the output of any of our test cases. It also gives use a diff
in pull requests. Of course you could do this with any test framework, but with jest updating
the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and
if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but
brittle example. For more involved situations you may wish to leverage
babel-traverse. It allows you to specify an object with a `visitor` key, exactly like
you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly.
Note that we're not using `assert` in the test. This ensures that if our plugin does
weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's
[RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests)
this should be familiar. You can look at
[the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md)
to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
================================================
FILE: translations/en/user-handbook.md
================================================
# Babel User Handbook
Written by [Jamie Kyle](https://jamie.build/)
This document covers everything you ever wanted to know about using
[Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for
a complete list.
# Table of Contents
- [Introduction](#toc-introduction)
- [Setting up Babel](#toc-setting-up-babel)
- [`babel-cli`](#toc-babel-cli)
- [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
- [`babel-register`](#toc-babel-register)
- [`babel-node`](#toc-babel-node)
- [`babel-core`](#toc-babel-core)
- [Configuring Babel](#toc-configuring-babel)
- [`.babelrc`](#toc-babelrc)
- [`babel-preset-es2015`](#toc-babel-preset-es2015)
- [`babel-preset-react`](#toc-babel-preset-react)
- [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
- [Executing Babel-generated code](#toc-executing-babel-generated-code)
- [`babel-polyfill`](#toc-babel-polyfill)
- [`babel-runtime`](#toc-babel-runtime)
- [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
- [Manually specifying plugins](#toc-manually-specifying-plugins)
- [Plugin options](#toc-plugin-options)
- [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
- [Making your own preset](#toc-making-your-own-preset)
- [Babel and other tools](#toc-babel-and-other-tools)
- [Static analysis tools](#toc-static-analysis-tools)
- [Linting](#toc-linting)
- [Code Style](#toc-code-style)
- [Documentation](#toc-documentation)
- [Frameworks](#toc-frameworks)
- [React](#toc-react)
- [Text Editors and IDEs](#toc-text-editors-and-ides)
- [Babel Support](#toc-babel-support)
- [Babel Forum](#toc-babel-forum)
- [Babel Chat](#toc-babel-chat)
- [Babel Issues](#toc-babel-issues)
- [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Introduction
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can
use (and create) the next generation of JavaScript, as well as the next
generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals
coming out with new features all the time. Using Babel will allow you to use
many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest
standards into a version that will work everywhere today. This process is known
as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function
syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax
extensions such as the JSX syntax for React and Flow syntax support for static
type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out
and create their own plugins using the full power of Babel to do whatever
they want.
*Even further* than that, Babel is broken down into a number of core modules
that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and
very diverse. Throughout this handbook I'll be covering both how built-in Babel
tools work as well as some useful things from around the community.
----
# Setting up Babel
Since the JavaScript community has no single build tool, framework, platform,
etc., Babel has official integrations for all of the major tooling. Everything
from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks
like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways
of setting up Babel, but you can also visit the interactive
[setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and
> `npm`. Before continuing any further you should be comfortable with these
> tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to
a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using
`--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you _can_ install Babel CLI globally on your machine, it's much better
to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of
Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are
working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want
> to uninstall the global copy by running:
>
> ```sh
> $ npm uninstall --global babel-cli
> ```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put
our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command
inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This
option will allow you to run Babel just by requiring files, which may integrate
with your setup better.
Note that this is not meant for production use. It's considered bad practice to
deploy code that gets compiled this way. It is far better to compile ahead of
time before deploying. However this works quite well for build scripts or other
things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with
Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling
every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile.
> As node is executing the file before Babel has a chance to compile it.
>
> ```js
> require("babel-register");
> // not compiled:
> console.log("Hello world!");
> ```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to
integrate Babel might be to use the `babel-node` CLI which largely is just a
drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to
deploy code that gets compiled this way. It is far better to compile ahead of
time before deploying. However this works quite well for build scripts or other
things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read
> the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
> section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the
`babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using
`babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the
AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to
https://babeljs.io/docs/usage/api/#options.
----
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do
anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of
> different ways, it doesn't do anything by default. You have to explicitly tell
> Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or
**presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration
file. All you need to do is create a `.babelrc` file at the root of your
project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the
> `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the
JavaScript standard, also known as ES6) to ES5 (the version available in most
JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard
through the TC39's (the technical committee behind the ECMAScript standard)
process.
This process is broken through a 5 stage (0-4) process. As proposals gain more
traction and are more likely to be accepted into the standard they proceed
through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
- `babel-preset-stage-0`
- `babel-preset-stage-1`
- `babel-preset-stage-2`
- `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset
> above.
Each of these presets requires the preset for the later stages. i.e.
`babel-preset-stage-1` requires `babel-preset-stage-2` which requires
`babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
----
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same
is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist
in every JavaScript environment.
```
Uncaught TypeError: Array.from is not a function
```
To solve this problem we use something called a
[Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a
polyfill is a piece of code that replicates a native api that does not exist in
the current runtime. Allowing you to use APIs such as `Array.from` before they
are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its
polyfill, along with a customized
[regenerator](https://github.com/facebook/regenerator) runtime for getting
generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper"
methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every
file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every
single file where they are needed.
----
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel
exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to
do something differently you manually specify plugins. This works almost exactly
the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are
running.
For a full list of official plugins see the
[Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been
[built by the community](https://www.npmjs.com/search?q=babel-plugin). If you
would like to learn how to write your own plugin read the
[Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For
example, many transforms have a "loose" mode which drops some spec behavior in
favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option
> in the coming weeks.
> [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools
that can help you debugging your code or integrate with tools. There are also
a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the
environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current
environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is
not available, it will fallback to `NODE_ENV`, and if that is not available it
will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`,
> `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms
> then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All
this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This
could be a preset for the specific
[node version](https://github.com/leebenson/babel-preset-node5) you are running,
or maybe a preset for your
[entire](https://github.com/cloudflare/babel-preset-cf)
[company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention
`babel-preset-*` (please be responsible with this namespace), and create two
files.
First, create a new `package.json` file with the necessary `dependencies` for
your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc`
file, replacing plugin/preset strings with `require` calls.
```js
module.exports = function () {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
----
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it
can be rather difficult navigating how to set it up with other tools. However,
we try to work closely with other projects in order to make the experience as
easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis
tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org),
because of this we maintain an official
[`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the
`parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the
[`babel-eslint`](https://github.com/babel/babel-eslint) or
[`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into
checking the style of the code itself. A core maintainer of both the Babel and
JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official
integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext`
option. So integrating Babel is as easy as:
```
$ jscs . --esnext
```
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the
[`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or
[`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using
[documentation.js](http://documentation.js.org) you can generate detailed API
documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest
syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs
around the future of the language. Because of this, there has been a lot of work
going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways
that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes
([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)).
Even further, React relies on Babel to compile it's JSX syntax, deprecating it's
own custom tooling in favor of Babel. You can start by setting up the
`babel-preset-react` package following the
[instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of
transforms
[built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the
[`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform)
plugin which combined with a number of
[React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms)
can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your
text editor doesn't support it then it can be a really bad experience. For this
reason you will want to setup your text editor or IDE with a Babel plugin.
- [Sublime Text](https://github.com/babel/babel-sublime)
- [Atom](https://atom.io/packages/language-babel)
- [Vim](https://github.com/jbgutierrez/vim-babel)
- [WebStorm](https://babeljs.io/docs/setup/#webstorm)
----
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to
ensure that people have all the resources they need to be successful. So we
provide a number of different channels for getting support.
Remember that across all of these communities we enforce a
[Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md).
If you break the Code of Conduct, action will be taken. So please read it and
be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick
around and support others. If you find someone asking a question you know the
answer to, take a few minutes and help them out. Try your best to be kind and
understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of
their forum software for free (and we love them for it!). If forums are your
thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate
support from the community then come chat with us at
[slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by
[Github](http://github.com).
You can see all the open and closed issues on
[Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
- [Search for an existing issue](https://github.com/babel/babel/issues)
- [Create a new bug report](https://github.com/babel/babel/issues/new)
or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all
the help we can get. Spending a few more minutes crafting a really nice bug
report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of
your setup is contributing to the problem. If your problem is a piece of input
code, try deleting as much code as possible that still causes an issue.
> [WIP]
================================================
FILE: translations/es-ES/README.md
================================================
# Manual de Babel
Este manual está dividido en dos partes:
* [Manual de usuario](user-handbook.md) - Cómo configurar Babel y más.
* [Manual de extensiones](plugin-handbook.md) - Cómo crear extensiones para Babel.
> Para futuras actualizaciones, sigue a [@thejameskyle](https://twitter.com/thejameskyle) en Twitter.
Si estás leyendo una traducción de este manual, es posible que encuentres secciones en inglés que aún no han sido traducidas. Si deseas contribuir a una de las traducciones deberás hacerlo a través de Crowdin. Por favor, lee las [directrices para contribuir](/CONTRIBUTING.md) para obtener más información. Encontrarás varias palabras en inglés que se corresponden con conceptos de programación. Si se tradujeran estas palabras a otros idiomas habría una falta de consistencia y fluidez al leer otra información sobre ellas. En muchos casos encontrarás la traducción literal seguida del término en inglés entre paréntesis `()`. Por ejemplo: Árboles de sintaxis abstracta (AST).
================================================
FILE: translations/es-ES/plugin-handbook.md
================================================
# Manual para Plugins Babel
Este documento abarca cómo crear [plugins](https://babeljs.io/docs/advanced/plugins/) para [Babel](https://babeljs.io).
[](http://creativecommons.org/licenses/by/4.0/)
Este manual está disponible en otros idiomas, mira el archivo [README](/README.md) para ver la lista completa.
# Tabla de contenido
* [Introducción](#toc-introduction)
* [Conceptos básicos](#toc-basics)
* [Árboles de sintaxis abstracta (AST)](#toc-asts)
* [Etapas de Babel](#toc-stages-of-babel)
* [Análisis](#toc-parse)
* [Análisis léxico](#toc-lexical-analysis)
* [Análisis sintáctico](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definiciones](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Escribiendo tu primer plugin para Babel](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introducción
Babel es un compilador multipropósito para Javascript. Más que eso es una colección de módulos que pueden ser usados en diferentes formas de análisis estático.
> Análisis estático es el proceso de analizar código sin ejecutarlo. (Análisis de código durante la ejecución se conoce como análisis dinámico). El propósito del análisis estático varía enormemente. Puede ser utilizado para compilar, resaltar ó transformar código. También en procesos de linting, optimización ó minificación, entre muchos otros.
Babel puede ser usado para construir diferentes tipos de herramientas, las cuales nos pueden ayudar a ser más productivos y escribir mejores programas.
> ***Para futuras actualizaciones, sigue a [@thejameskyle](https://twitter.com/thejameskyle) en Twitter.***
* * *
# Conceptos básicos
Babel es un compilador de JavaScript, específicamente un compilador de fuente a fuente, a menudo llamado "transpiler". Esto significa le entregas a Babel código en JavaScript, Babel lo modifica y devuelve el código nuevo creado en Javascript.
## ASTs
Cada uno de estos pasos implican crear o trabajar con un [Árbol de Sintaxis Abstracta](https://en.wikipedia.org/wiki/Abstract_syntax_tree) ó AST por sus siglas en inglés.
> Babel utiliza un AST modificada de [ESTree](https://github.com/estree/estree), con la especificación base disponible [aquí](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Si deseas tener una mejor idea de los nodos AST, visita [AST Explorer](http://astexplorer.net/). [Aquí](http://astexplorer.net/#/Z1exs6BWMq) hay un enlace que contiene el código del ejemplo anterior.
Este mismo programa puede ser representado en un árbol como este:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
O como un objeto de JavaScript:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Te darás cuenta de que cada nivel del AST tiene una estructura similar:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Nota: Algunas propiedades se han eliminado por simplicidad.
Cada uno de estos se conocen como un **Nodo**. Un AST puede ser formado por un sólo nodo, ó cientos si no miles de nodos. Juntos tienen la capacidad de describir la sintaxis de un programa que puede utilizarse para análisis estático.
Cada nodo tiene esta interfaz:
```typescript
interface Node {
type: string;
}
```
El campo `tipo` es una cadena que representa que tipo de nodo es el objeto. Por ejemplo: `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Cada tipo de nodo define un conjunto adicional de propiedades que describen ese tipo de nodo en particular.
Hay propiedades adicionales en cada nodo que Babel genera para describir la posición del nodo en el código fuente original.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Estas propiedades ` start `, `end`, `loc`, aparecen en cada uno de los nodos.
## Etapas de Babel
Las tres etapas principales de Babel son **analizar**, **transformar**, **generar**.
### Analizar
La etapa de **análisis**, recibe código a la entrada y entrega un AST a la salida. Hay dos fases de análisis en Babel: [**Análisis léxico**](https://en.wikipedia.org/wiki/Lexical_analysis) y [**Análisis sintáctico**](https://en.wikipedia.org/wiki/Parsing).
#### Análisis léxico
El análisis léxico recibe una cadena de código y lo convierte en una secuencia de **tokens**.
Podemos pensar los tokens como una matriz plana, en la cual cada elemento representa una parte de sintaxis del lenguaje.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Aquí cada uno de los `tipo`s tienen un conjunto de propiedades que describen el token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Así como los nodos AST, estos también tienen `start`, `end` y `loc`.
#### Análisis sintáctico
El análisis sintáctico recibe una secuencia de tokens y lo convierten en una representación de AST. Usando la información de los tokens, esta fase les cambia el formato y los representa en un AST. El cual representa la estructura del código de una manera más sencilla para trabajar.
### Transformar
La etapa de [transformación](https://en.wikipedia.org/wiki/Program_transformation) toma un AST y cruza a través de él, agregando, actualizando y quitando nodos a medida que va avanzando. Esta es, por lejos, la parte más compleja de Babel o cualquier otro compilador. Aquí es donde los plugins operan, así que este será el tema pricncipal de este manual. Por ahora no lo analizaremos en detalle.
### Generar
La etapa de [generación de código](https://en.wikipedia.org/wiki/Code_generation_(compiler)) toma el AST y lo convierte de vuelta en una cadena de código, también creando el [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Generar el código es bastante simple: se recorre a través del AST, empezando en lo más profundo, construyendo una cadena que representa el código transformado.
## Recorrido
Cuando se desea transformar un AST es necesario [recorrer el árbol](https://en.wikipedia.org/wiki/Tree_traversal) de manera recursiva.
Digamos que tenemos el tipo `FunctionDeclaration`. Este tiene unas propiedades: `id`, `params` y `body`. Cada propiedad tiene nodos anidados.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Así que empezamos en `FunctionDeclaration`. Sabemos sus propiedades internas, entonces visitamos ordenadamente cada una de ellas y sus respectivos hijos.
A continuación vamos a `id` que es un ` Identifier`. Los ` Identifier`s no tienen nodos hijos en sus propiedades por lo tanto continuamos el recorrido.
Después encontramos ` params ` que es una arreglo de nodos así que visitamos cada uno de ellos. En este caso es sólo un nodo que es también un `Identifier`, entonces seguimos.
Entonces encontramos `body` que es un `BlockStatement` con una propiedad `body` que es un arreglo, por lo tanto vamos a cada uno de ellos.
Aquí el único elemento es el nodo `ReturnStatement` que tiene un `argument`. Vamos a `argument` y encontramos una `BinaryExpression`.
La `BinaryExpression` tiene tres propiedades ` operator`, `left` y `right`. El operador no es un nodo, es sólo un valor, entonces no lo visitamos. En cambio, vistamos `left` y `right`.
Este proceso de recorrido, en Babel, pasa a lo largo de la etapa de transformación.
### Visitantes
Cuando decimos que "vamos" a un nodo, lo que realmente queremos decir es que los estamos **visitando**. Se utiliza éste término porque existe el concepto de [**visitante**](https://en.wikipedia.org/wiki/Visitor_pattern) (visitor).
Los visitantes (visitors) son patrones usados en AST transversal en distintos idiomas. Básicamente son objetos, con métodos definidos para aceptar ciertos tipos de nodos en un árbol. Esto es un poco abstracto, veamos un ejemplo.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// También puedes crear un visitador y el método agregar después
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Nota:**`Identifier() { ... }` es la abreviatura de `Identifier: { enter() { ... } }`.
Esto es un visitador básico que cuando se usa durante un recorrido llamara el método `Identifier()` por cada `Identifier` en el árbol.
Así que, el código del método `Identifier()` será llamado cuatro veces por cada `Identifier` (incluyendo `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
Estas llamadas son por cada nodo cuando **entrar** al nodo. Sin embargo existe la posibilidad de llamar al método del visitador cuando **sale**.
Imagina que nosotros tememos una estructura así:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
Según atravesamos hacia abajo cada rama del árbol eventualmente llega a un punto final donde nosotros atravesamos de retroceso el árbol para llegar al siguiente nodo. Yendo hacia abajo del árbol nosotros **entramos ** en cada nodo, entonces yendo de retroceso nosotros **salimos** de cada nodo.
Vamos a *caminar* a traves el proceso de lo que parece el árbol de arriba.
* Entra `FunctionDeclaration`
* Entra `Identifier (id)`
* Punto final
* Sale ` Identifier (id)`
* Entra ` Identifier (params[0])`
* Punto final
* Sale ` Identifier (params[0])`
* Entra `BlockStatement (body)`
* Entra `ReturnStatement (body)`
* Entra `BinaryExpression (argument)`
* Entra `Identifier (left)`
* Punto final
* Sale `Identifier (left)`
* Entra `Identifier (right)`
* Punto final
* Sale `Identifier (right)`
* Sale `BinaryExpression (argument)`
* Sale `ReturnStatement (body)`
* Sale `BlockStatement (body)`
* Sale `FunctionDeclaration`
Así que cuando se crea un visitador tu tienes dos oportunidades de visitar un nodo.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
Si es necesario, tu puedes también aplicar la misma función por múltiples nodos de visitante separándolos con un `|` en el método como cadena de la siguiente manera `Identifier|MemberExpression`.
Ejemplo en el uso del plugin [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47)
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
También puedes usar alias como nodos visitadores (como se define en [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
Por ejemplo,
`Function` es un álias para `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
En AST generalmente tiene muchos Nodos, pero, Cómo los nodos se relacionan unos con otros? Podríamos tener un enorme objeto mutable que puedas manipular y al que tener acceso total, o simplemente esto con **Paths**.
Un **Path** es una representación de un objeto entre el enlace de dos nodos.
Por ejemplo, si tomamos el siguiente nodo y su hijo:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
Y representamos el hijo `Identifier` como un path, se mira como algo asi:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
También tiene metadatos adicionales sobre el path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
También toneladas y toneladas de métodos relacionados con agregación, actualización, movimiento y eliminación de nodos, pero, entraremos en esos mas adelante.
En un sentido, paths son una representación **reactiva** de la posición de un nodo en el árbol y toda clase de información sobre el nodo. Siempre que tu llames un método que modifica el árbol, esta información es actualizada. Babel maneja todo esto por ti para poder trabajar con los nodos lo mas descentralizado posible.
#### Paths en Visitadores
Cuando tu tienes un visitador que contiene el `Identifier()`, estas actualmente visitando el camino en vez del nodo. De esta manera tu estas mayormente trabajando con la representación reactiva del nodo en vez del nodo en si mismo.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Estado
Estado es el enemigo de la transformación AST. El estado te morderá una y otra vez y tus suposiciones sobre el estado casi siempre serán probadas incorrectas por alguna sintaxis que no consideraste.
Toma el siguiente código:
```js
function square(n) {
return n * n;
}
```
Vamos a escribir rapidamente un visitador que renombre `n` a `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
Esto podría funcionar con el código de arriba, pero nosotros podemos fácilmente romperlo haciendo esto:
```js
function square(n) {
return n * n;
}
n;
```
La mejor manera de lidiar con esto es la recursion. Así que vamos a hacer una película de Christopher Nolan y pongamos un visitador dentro de un visitador.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Por su puesto, este es un ejemplo inventado, pero que demuestra como eliminar el estado global desde uno de tus visitadores.
### Scope
Ahora vamos a presentar el concepto de [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). Javascript tiene un [lexical scope](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), cual es una estructura de árbol de bloques donde se crean nuevos scopes.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
En Javascript, cada referencia creada, ya sea a una variable, función, clase, parámetro, import, etiqueta, etc, pertenece al ámbito actual.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Código dentro de un ámbito más profundo puede llegar a usar una referencia de un ámbito anterior.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
Un ámbito menor puede incluso crear una referencia de el mismo nombre sin modificarla.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
Cuando escribimos una transformación queremos estar alerta con el ámbito. Necesitamos estar seguros de no romper código existente mientras modificamos las diferentes partes de este.
Podríamos querer incluir nuevas referencias y estar seguros de que no choquen con las existentes. O tal vez solo queramos encontrar donde una variable está siendo referenciada. Queremos tener la capacidad de rastrear estas referencias dentro de un ámbito determinado.
Un ámbito puede ser representado como:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
Cuando crea un nuevo ámbito, lo hace asignándole una ruta y un ámbito padre. Entonces durante el proceso de recorrido recopila todas las referencias ("enlaces") dentro de ese ámbito.
Una vez haya terminado, hay todo tipo de métodos que puede usar en ese ámbito. Hablaremos de estos más adelante.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definiciones
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validadores
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Convertidores
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Escribiendo tu primer plugin para Babel
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/es-ES/user-handbook.md
================================================
# Manual de usuario de Babel
Este documento abarca todo lo que siempre quizo saber sobre como usar [Babel](https://babeljs.io) y sus herramientas relacionadas.
[](http://creativecommons.org/licenses/by/4.0/)
Este manual está disponible en otros idiomas, revise el [README](/README.md) para que pueda visualizar la lista completa.
# Tabla de Contenidos
* [Introducción](#toc-introduction)
* [Iniciando Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Ejecutar Babel CLI dentro de un proyecto](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuración de Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Ejecutar código generado en Babel](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuración de Babel (Avanzado)](#toc-configuring-babel-advanced)
* [Especificar extensiones manualmente](#toc-manually-specifying-plugins)
* [Opciones de extensiones](#toc-plugin-options)
* [Personalizar Babel basado en el entorno](#toc-customizing-babel-based-on-environment)
* [Haciendo su propio preset](#toc-making-your-own-preset)
* [Babel y otras herramientas](#toc-babel-and-other-tools)
* [Herramientas de análisis estático](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Estilo de código](#toc-code-style)
* [Documentación](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Editores de texto e IDEs](#toc-text-editors-and-ides)
* [Soporte de Babel](#toc-babel-support)
* [Foro de Babel](#toc-babel-forum)
* [Chat de Babel](#toc-babel-chat)
* [Problemas en Babel](#toc-babel-issues)
* [Crear un sorprendente reporte de fallos de Babel](#toc-creating-an-awesome-babel-bug-report)
# Introducción
Babel es un compilador multi propósito para Java Script. Al usar Babel usted puede usar (y crear) la siguiente generación de JavaScript, así como la siguiente generación de herramientas para JavaScript.
JavaScript es un lenguaje en constante evolución, con nuevas especificaciones, propuestas en camino y nuevas funcionalidades todo el tiempo. Usar Babel le permitirá usar muchas de estás características años antes de que estén disponibles en todos lados.
Babel hace esto al compilar código en JavaScript escrito con los últimos estándares a una versión que funcionará en todos lados hoy. Este proceso es conocido como compilación source-to-source, también conocido como transpiling.
Por ejemplo, Babel puede transformar la nueva función flecha de ES2015 de esto:
```js
const square = n => n * n;
```
A esto:
```js
const square = function square(n) {
return n * n;
};
```
Sin embargo, Babel puede hacer mucho más que esto porque Babel tiene soporte para extensiones de sintaxis como JSX para React y soporte para la sintaxis de Flow para validación de tipado estático.
Más allá de eso, todo en Babel es simplemente una extensión y cualquiera puede crear sus propias extensiones usando todo el poder de Babel para hacer cualquier cosa que se deseé.
*Aún más allá* que eso, Babel está conformado por un numero de módulos clave que cualquiera puede usar para construir la siguiente generación de herramientas para JavaScript.
Muchas personas hacen eso también, el ecosistema que se ha formado alrededor de Babel es masivo y muy diverso. A lo largo de este manual estaré cubriendo tanto como las herramientas de Babel predefinidas funcionan y algunas cosas útiles alrededor de la comunidad.
> ***Para futuros updates, siga a [@thejameskyle](https://twitter.com/thejameskyle) en Twitter.***
* * *
# Iniciando Babel
Debido a que la comunidad de JavaScript no tiene una sola herramienta, framework o plataforma etc., Babel tiene integraciones oficiales con las herramientas de trabajo más comunes. Todo desde Gulp hasta Browserify, desde Ember a Meteor, no importa como su configuración se vea, existe probablemente una integración oficial.
Para los propósitos de este manual, solo se van a cubrir las partes incorporadas por default para configurar Babel, pero usted puede visitar también la [página de configuración](http://babeljs.io/docs/setup) para todas las integraciones.
> **Nota:** Esta guía usará referencias recurrentes a herramientas de la linea de comandos como `node` y `npm`. Antes de continuar usted debe sentirse cómodo con estas tecnologías.
## `babel-cli`
Babel CLI es una forma simple de compilar archivos con Babel desde la linea de comandos.
Vamos a instalarlo primero globalmente para aprender lo básico.
```sh
$ npm install --global babel-cli
```
Podemos compilar nuestro primer archivo de esta manera:
```sh
$ babel my-file.js
```
Esto arrojará el archivo compilado directamente a su terminal. Para escribir la salida a un archivo vamos a especificar `--out-file` o `o`.
```sh
$ babel example.js --out-file compiled.js
# o
$ babel example.js -o compiled.js
```
Si deseamos compilar un directorio completo a un nuevo directorio podemos hacerlo usando: `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# o
$ babel src -d lib
```
### Ejecutar Babel CLI dentro de un proyecto
Si bien usted *puede* instalar Babel CLI globalmente en su máquina, es mucho mejor instalarlo **localmente** en cada proyecto.
Hay dos principales razones para esto.
1. Diferentes proyectos en la misma máquina pueden depender de diferentes versiones de Babel, permitiéndole a usted actualizar uno por uno.
2. Esto significa que usted no tiene una dependencia implícita en el ambiente en que usted esta trabajando, haciendo su proyecto más portable y fácil de configurar.
Podemos instalar Babel CLI localmente ejecutando:
```sh
$ npm install --save-dev babel-cli
```
> **Nota:** Puesto que es generalmente una mala idea correr Babel global puede des instalar la copia global:
>
> ```sh
$ npm uninstall --global babel-cli
```
Después de terminarse de instalar, su archivo `package.json` debería verse algo así:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Ahora, en vez de ejecutar Babel directamente desde la línea de comandos, vamos a escribir nuestros comandos en **npm scripts** los cuales usarán nuestra versión local.
Simplemente añada el campo `"scripts"` a su paquete `package.json` y ponga el comando de babel dentro como `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Ahora desde nuestro terminal podemos correr lo siguiente:
```js
npm run build
```
Esto ejecutará Babel igual que antes, sólo que ahora estamos usando una copia local.
## `babel-register`
El siguiente método más común de ejecutar Babel es a través de `babel-register`. Esta opción te permitirá ejecutar babel solo requiriendo archivos, en esa manera se configurará de mejor manera.
Nótese que esto no quiere decir que sea para uso en Producción. Es considerado una mala practica desplegar código compilado en esa manera. Es muchísimo mejor compilar el código antes de desplegar. Como sea, este trabajo es recomendado para script con el fin de ser ejecutados localmente.
Primero vamos a crear un `index.js` en nuestro proyecto.
```js
console.log("Hello world!");
```
Si fueramos a ejecutar esto con `node index.js` no compilaria con Babel. Así que en lugar de hacer eso, vamos a configurar `babel-register`.
Primero instala `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Siguiente paso, crea un archivo `register.js` en tu proyecto y escribe el siguiente código:
```js
require("babel-register");
require("./index.js");
```
Lo que esto hace es *registrar* Babel en el modulo de sistema en Node y empieza a compilar cada vez que es requerido `` `required` ``.
Ahora, en vez de ejecutar `node index.js` nosotros podemos usar `register.js`.
```sh
$ node register.js
```
> **Nótese** Usted no puede registrar Babel en el mismo archivo tu quieres compilar. Cada nodo es ejecutado antes que Babel ha tenido oportunidad de compilarlo.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
Si usted esta ejecutando algún código via linea de comandos `node` la forma mas fácil de integrar Babel podría ser usando `babel-node` CLI cual es es un reemplazo para `node` CLI.
Nótese que esto no quiere decir que sea para uso en Producción. Es considerado una mala practica desplegar código compilado en esa manera. Es muchísimo mejor compilar el código antes de desplegar. Como sea, este trabajo es recomendado para script con el fin de ser ejecutados localmente.
Primero asegúrese que ha instalado `babel-cli`.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Entonces reemplace donde sea usted este ejecutando `node` debe ser con `babel-node`.
Si usted esta ejecutando `scripts` con npm, usted puede hacer simplemente esto:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
De otra manera, usted tendrá que escribir la localización de `babel-node` en su sistema de archivos.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Truco: Usted puede usar también [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
Si usted necesita usar Babel mediante programación por cualquier razón, usted puede usar `babel-core` en si mismo.
Primero instala `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
Si usted tiene una cadena en Javascript puede compilarla directamente usando `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
Si usted esta trabajando con archivos, usted puede usar también el api asíncrono:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
O el api síncrono:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
Si usted ya ha usado Babel AST for cualquier razón, podría transformarlo desde AST directamente.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuración de Babel
Usted habrá notado por ahora que ejecutando Babel en si mismo no pareciera hacer nada mas que copiar archivos Javascript de una localización a otra.
Esto es porque nosotros no hemos dicho a Babel hacer nada aún.
> Desde que el propósito general de Babel es compilar que usado en muchísimas formas diferentes, no hace nada por defecto. Usted tiene que decirle explícitamente a Babel que es lo que debería hacer.
Sted puede darle a Babel instrucciones sobre lo que hacer instalando **plugins** o **presets** (grupos de extensiones).
## `.babelrc`
Antes de empezar a decirle a Babel que hacer. Nosotros necesitamos crear un archivo de configuración. Todo lo que usted necesita es crear un archivo `.babelrc` en la raíz de su proyecto. Empezando con algo como esto:
```js
{
"presets": [],
"plugins": []
}
```
Este archivo es como usted configuras Babel para hacer lo que usted quiere.
> **Nótese:** Mientras usted puede pasarle opciones a Babel en otras vías diferentes que `.babelrc`, esta es la mejor manera de hacerlo.
## `babel-preset-es2015`
Vamos a empezar a decirle a babel que compile de ES2015 (la nueva versión del estándar Javascript, también conocida como ES6) a ES5 (la versión disponible en la mayor parte de los ambientes hoy en día).
Vamos a hacer la instalación del ajuste "es2015" de Babel:
```sh
$ npm install --save-dev babel-preset-es2015
```
Luego vamos modificar nuestro `.babelrc` para incluir nuestro ajuste.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Configurando React es muy facil. Solo debemos instalar la libreria de ajuste:
```sh
$ npm install --save-dev babel-preset-react
```
Luego agregar la configuración de ajuste al archivo `.babelrc`:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
Javascript también dispone algunas propuestas que están en camino de ser estándar a través de el proceso TC39's (el comité técnico detrás del estándar ECMAScript).
El proceso esta dividido en 5 etapas (0-4). Como propuestas ganan mas atracción y tienen mas opciones de ser aceptadas dentro del estándar según van pasando por varias etapas, finalmente son aceptadas en el estándar al llegar a la 4ta etapa.
Estos son los conjuntos en babel para las 4 diferentes ajustes:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Nótese que no hay stage-4, ya que es considerada simplemente como el ajuste `es2015` de arriba.
Cada una de las ajustes requiere un ajuste posterior definido. Ejemplo. `babel-preset-stage-1` requiere de `babel-preset-stage-2` que a su vez requiere de `babel-preset-stage-3`.
Simplemente instalando las etapas en las que usted esta interesado en usar:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Luego usted puede agregarlas a su archivo `.babelrc` de configuración.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Ejecutar código generado en Babel
Hasta el momento, usted ha compilado código con Babel, pero, esto no es el fin de la historia.
## `babel-polyfill`
Casi toda la sintaxis futurista de Javascript puede ser compilada con Babel, pero, no es lo mismo para los APIs.
Por ejemplo, el siguiente código tiene una función de flecha que necesita ser compilada:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
El cual nos retorna en algo así:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
Como sea, esto no va a funcionar en todos lados porque `Array.from` no existe en todos los ambientes de ejecución de Javascript.
Uncaught TypeError: Array.from is not a function
Para resolver este problema debemos usar algo llamado [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simplemente un polyfill es una pieza de código que replica un comportamiento nativo del api que no existe en el ambiente de ejecución. Permitiéndote usar el APIs mas allá de `Array.from` antes que este disponible.
Babel usa el excelente [core-js](https://github.com/zloirock/core-js) como polyfill, además de un personalizado [regenerator](https://github.com/facebook/regenerator) para poder usar generadores y funciones async.
Para incluir un polyfill en Babel, primero debe instalarlo con npm:
```sh
$ npm install --save babel-polyfill
```
Luego, simplemente incluye el polyfill en la parte superior de cualquiera de los archivos que lo requieran:
```js
import "babel-polyfill";
```
## `babel-runtime`
Con el fin de implementar detalles de la especificación de ECAMScript, Babel usara funciones "helper" con el fin the mantener limpio el código generado.
Estos `helpers` pueden llegar a ser muy extensos y son agregados en la parte superior de cada archivo, puedes moverlos en un único ambiente de ejecución `runtime` a parte cuando cuando sean requeridos.
Empieza instalando `babel-plugin-transform-runtime` y `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Luego, actualiza su `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Ahora, Babel compilará el código como el siguiente:
```js
class Foo {
method() {}
}
```
En esto:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
En vez de poner `_classCallCheck` y `_createClass` en cada archivo cuando son necesitados.
* * *
# Configuración de Babel (Avanzado)
La mayor parte de las personas pueden usar Babel solo con los ajustes incorporados, pero Babel ofrece mucho más poder más que eso.
## Especificar extensiones manualmente
Los ajustes de Babel son simplemente colecciones de extensiones pre-configurados, si usted quiere hacer algo diferente usted puede especificar manualmente extensiones. Esta tarea es muy parecida a la forma de los ajustes.
Primero instalamos la extensión:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Luego agrega el campo `plugins` en tu `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
Esto le da un control refinado sobre la transformación exacta que usted esta ejecutando.
Para una lista completa de extensiones vea la [página de extensiones de Babel](http://babeljs.io/docs/plugins/).
También eche un ojo a todos las extensiones que han sido construidos [por la comunidad](https://www.npmjs.com/search?q=babel-plugin). Si usted le gustaria aprender como escribir su propio plugin lea el [Babel Plugin Handbook](plugin-handbook.md).
## Opciones de extensiones
Muchos de los plugin también son opciones de configuración que se comportan diferente. Por ejemplo, muchas transformaciones tienen un modo de "loose" cuando reduce el comportamiento de la especificación en favor the una simple y mejor rendimiento de código generado.
Para agregar opciones al plugin, simplemente tiene que hacer el siguiente cambio:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> Voy a estar trabajando en actualizaciones the la documentación de extensiones para detallar cada opción en la siguientes semanas. [Sígueme para obtener las actualizaciones](https://twitter.com/thejameskyle).
## Personalizar Babel basado en el entorno
Las extensiones de Babel resuelve muchas tareas diferentes. Muchos de ellos son herramientas de desarrollo que pueden ayudar a debuguear su código o integración con herramientas. Hay también muchas extensiones que solo se enfocan en optimizar su código en producción.
Para esta razón, es común que quieras una configuración de Babel basado en un ambiente. Usted puede fácilmente hacerlo en su archivo `.babelrc`.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel habilitará una configuración dentro de `env` basado en su ambiente actual.
El ambiente actual usara `process.env.BABEL_ENV`. Cuando `BABEL_ENV` no este disponible, se usara como ultimo recurso `NODE_ENV`, y si tampoco esta disponible por defecto será `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Nótese:**`[COMMAND]` es cualquier cosa que usted ejecute en Babel. (ie. `babel`, `babel-node`, o tal vez solo `node` si usted esta usando el enganche).
>
> **Truco**: Si usted quiere su commando funcione en multi-plataforma unix y windows entonces use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Haciendo su propio preset
Especificar extensiones manualmente? Opciones en extensiones? Configuración parametrizada? Todas estas configuraciones podrían llegar a ser toneladas de duplicaciones en todos sus proyectos.
For esta razón, nosotros animamos a la comunidad a crear sus propios ajustes. Esto podria ser un ajuste especifico [node version](https://github.com/leebenson/babel-preset-node5) que ustead ejecutando, o tal vez un ajuste para [toda](https://github.com/cloudflare/babel-preset-cf) su [compañia](https://github.com/airbnb/babel-preset-airbnb).
Es fácil crear un ajuste, tenemos el archivo `.babelrc`:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
Todo lo que usted necesita hacer es crear un nuevo proyecto siguiendo la convención de nombre `babel-preset-*` (por favor sea responsable con el espacio de nombre), y cree dos archivos.
Primero, cree un archivo nuevo `package.json` con las `dependencies` para su ajuste.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Luego, cree un archivo `index.js` que exportará el contenido a su archivo `.babelrc`, reemplace su plugin/preset strings con la llamada `require`.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Después, simplemente publique en npm y usted podría usarlo como cualquier otro ajuste.
* * *
# Babel y otras herramientas
En Babel es bastante sencillo configurar una vez te enganchas de el, pero puede ser mas bien difícil ver la manera en que se configura con otras herramientas. Como sea, nosotros tratamos de trabajar de cerca con otros proyectos con el fine the hacer esta experiencia lo mas fácil posible.
## Herramientas de análisis estático
Nuevos estándares traen mucha nueva sintaxis al lenguaje y herramientas análisis estático están empezando a tomar ventaja de ello.
### Linting
Una de las herramientas mas populares es [ ESLint](http://eslint.org), debido a esto nosotros mantenemos la integración oficial [`babel-eslint`](https://github.com/babel/babel-eslint).
Primero instala `eslint` y `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Luego crea o usa el archivo existente `.eslintrc`en tu proyecto y configura el `parser` y `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Ahora agrega la tarea `lint` en tus scripts del`package.json` en npm:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Luego solo ejecuta la tarea y tu ya habrás configurado todo.
```sh
$ npm run lint
```
Para mas información consulta la documentación [`babel-eslint`](https://github.com/babel/babel-eslint) o [`eslint`](http://eslint.org).
### Estilo de código
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS es una herramienta extremadamente popular para ir mas alla de revisar el estilo de código en si mismo. Un contribuidor del core de Babel y JSCS ([@hzoo](https://github.com/hzoo)) mantiene la integración oficial de JSCS.
Incluso mejor, esta integración vive ahora dentro de JSCS debajo de la opcion `--esnext`. Así que integrar Babel es incluso mas fácil:
$ jscs . --esnext
Desde la linea de comando o agregando la opcion `esnext` en el archivo `.jscsrc`.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
Para mas informacion consulta la documentación [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) o [`jscs`](http://jscs.info).
### Documentación
Usando Babel, ES2015, y Flow usted puede you can inferir mucho en su código. Usando [documentation.js](http://documentation.js.org) usted puede generar una documentación de su API detallado muy fácilmente.
Documentation.js usa Babel entre bastidores para soportar lo ultimo en sintaxis incluyendo la ultima anotaciones y sintaxis de Flow con el fin de declarar "types" en su código.
## Frameworks
La gran mayoría de los frameworks de Javascript están enfocados ahora a alinear sus API alrededor del futuro del lenguaje. Debido a esto, ha habido mucho trabajo en la herramienta.
Frameworks tienen la oportunidad no solo de usar Babel, pero extendiéndolo con el fin de mejorar la experiencia de sus usuarios.
### React
React ha cambiado dramáticamente su API alienándolo con con classes de ES2015 ([Lee sobre la actualización de su API aquí](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Incluso mas allá, React confía en Babel para compilar su sintaxis JSX devaluando su antigua herramienta en favor de Babel. Usted puede empezar configuración el paquete `babel-preset-react` siguiendo las [instrucciones arriba](#babel-preset-react).
La comunidad de React ha tomado took Babel y han avanzado con el. Hay ahora un gran número de transformaciones [construidas por la comunidad](https://www.npmjs.com/search?q=babel-plugin+react).
Mas notablemente el plugin [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) que combinado con un numero de [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) pueden permitir cosas como *módulo de recarga instantánea* y otras herramientas para debuguear.
## Editores de texto e IDEs
Presentar ES2015, JSX, y la sintaxis Flow con Babel puede ser de mucha ayuda, pero si su editor de texto no lo soporta entonces puede convertirse en una mala experiencia. Por esta razón, usted querrá configurar su editor de texto o IDE con e plugin de Babel.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Soporte de Babel
Babel ha tenido un crecimiento rápido y grande en la comunidad, a medida que crecemos nosotros queremos asegurarnos que la gente tiene todos los recursos que necesita para ser éxito. Así que nosotros proveemos un numero de canales diferentes para obtener soporte.
Recuerde que a través de las comunidades nosotros hacemos cumplir el [Código de Conducta](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). Si usted no cumple con el Código de Conducta, acciones serán tomadas. Asi que por favor, lea cuidadosamente y sea responsable cuando usted interactúa con otros.
Nosotros también buscamos el crecimiento entre la comunidad para ayudarse mutuamente, para gente que busca ayuda en otros. Si usted encuentra alguien preguntando al que usted puede responder, tómese unos minutos para ayudarle. Trate de ser dar lo mejor para ser amable y comprensible en el proceso.
## Foro de Babel
Discourse0> nos ha proveído una version alojada para nuestro foro gratuitamente (y los amamos por ello ¡¡). Si el foro es mas de lo suyo, haga una parada por [discuss.babeljs.io](https://discuss.babeljs.io).
## Chat de Babel
Todo el mundo ama [Slack](https://slack.com). Si used esta buscando por ayuda inmediata de la comunidad entonces venga a chatear con nosotros en [slack.babeljs.io](https://slack.babeljs.io).
## Problemas en Babel
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
Si usted desea abrir la resolución de un nuevo fallo:
* [Busca por un error existente](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Crear un sorprendente reporte de fallos de Babel
Los fallos en Babel pueden ser a veces muy difíciles de probar en remoto, así que necesitamos toda la ayuda que podamos tener. Tomándose unos minutos para detallar el reporte de un fallo puede ayudar que su problema sea resuelto significativamente más rápido.
Primero, trata de aislar su problema. Es extremadamente It's extremely improvable que cada parte de tu configuración contribuya al problema. Si su problema es un trozo de código, trate de eliminar la mayor cantidad de código posible que pueda causar el problema.
> [WIP]
* * *
> ***Para futuras actualizaciones, sigue a [@thejameskyle](https://twitter.com/thejameskyle) en Twitter.***
================================================
FILE: translations/fa-IR/README.md
================================================
# کتاب راهنمای Babel
این کتاب راهنما به دو بخش تقسیم شده است:
* [راهنمای کاربر](user-handbook.md) - نحوه ی آماده سازی و پیکربندی Babel و اطلاعات بیشتر.
* [راهنمای Plugin](plugin-handbook.md) چگونه برای Babel افزونه ایجاد کنید.
> برای به روز رسانی آینده [@thejameskyle](https://twitter.com/thejameskyle) را در توییتر دنبال کنید.
اگر شما در حال خواندن ترجمه غیر انگلیسی این کتاب هستید، ممکن هست با بخشی مواجه شوید که هنوز ترجمه نشده باشد. اگر شما قصد دارید در کار ترجمه همکاری داشته باشید، باید از Crowdin برای اینکار استفاده کنید. لطفا [راهنمای همکاری](/CONTRIBUTING.md) را برای اطلاعات بیشتر مطالعه فرمائید. شما در حین ترجمه به تعدادی واژه انگلیسی که مفاهیم برنامه نویسی هستند، برخواهید خورد. اگر قصد ترجمه این واژه ها به زبان دیگری را داشتید، سعی کنید واژه های روان و سلیس را انتخاب کنید که خواننده به خوبی متوجه آنها شود. در بسیاری از موارد شما ترجمه تحت اللفظی از کلمه انگلیسی را در پرانتز `()` مشاهده خواهید کرد. به عنوان مثال: درخت نحو انتزاعی (ASTs).
================================================
FILE: translations/fa-IR/plugin-handbook.md
================================================
# Babel Plugin Handbook
This document covers how to create [Babel](https://babeljs.io) [plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduction](#toc-introduction)
* [Basics](#toc-basics)
* [ASTs](#toc-asts)
* [Stages of Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Lexical Analysis](#toc-lexical-analysis)
* [Syntactic Analysis](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Writing your first Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introduction
Babel is a generic multi-purpose compiler for JavaScript. More than that it is a collection of modules that can be used for many different forms of static analysis.
> Static analysis is the process of analyzing code without executing it. (Analysis of code while executing it is known as dynamic analysis). The purpose of static analysis varies greatly. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
You can use Babel to build many different types of tools that can help you be more productive and write better programs.
> ***برای به روز رسانی آینده [@thejameskyle](https://twitter.com/thejameskyle) را در توییتر دنبال کنید.***
* * *
# Basics
Babel is a JavaScript compiler, specifically a source-to-source compiler, often called a "transpiler". This means that you give Babel some JavaScript code, Babel modifies the code, and generates the new code back out.
## ASTs
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Stages of Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Parse
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntactic Analysis
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transform
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Writing your first Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/fa-IR/user-handbook.md
================================================
# کتاب راهنمای کاربر Babel
در این نوشتار شما با تمام مفاهیم [Babel](https://babeljs.io) و همچنین ابزرهای مربوط به آن، مطلع خواهید شد.
[](http://creativecommons.org/licenses/by/4.0/)
این کتاب راهنما همچنین به زبان های دیگر در دسترس است، لیست کامل در [README](/README.md).
# فهرست محتویات
* [مقدمه](#toc-introduction)
* [راه اندازی Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [پیکربندی Babel](#toc-configuring-babel)
* [`babelrc.`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# مقدمه
Babel یک کامپایلر چند منظوره و کلی برای جاوا اسکریپت است. با استفاده از Babel شما میتوانید از نسل ها و نسخه های جلوتر جاوا اسکریپت و همچنین ابزارهای مربوط به جاوا اسکریپت استفاده (و یا تولید) کنید.
جاوا اسکریپت به عنوان یک زبان به صورت ثابت در حال تکامل است، همچنین با قابلیت های جدیدی که همراه با مشخصات و پیشنهادات جدیدیشان تماما در حال انتشار و اضافه شدن به این زبان هستند. استفاده از Babel به شما این اجازه را می دهد که از تعداد بیشماری از این قابلت ها و خاصیت ها حتی در صورتی که همه جا قابل به کار نیستند، استفاده کنید.
Babel این کار را فرضا با کامپایل(و یا تبدیل) کد جاوا اسکریپت نوشته شده بر اساس آخرین استاندارد ها به کد جاوا اسکریپت با نسخه ای که امروز مورد استفاده در دستگاه ها است، انجام می دهد. این فرایند به کامپایل منبع-به-منبع و همچنین ترانسپایل(از سطحی به سطح دیگر) شناخته شده است.
به عنوان مثال، Babel میتواند تابع پیکان دار که در نسخه ES2015 اضافه شده را، از این کد پیش رو:
```js
;const square = n => n * n
```
به این کد ترجمه و یا تبدیل کند:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***برای به روز رسانی آینده [@thejameskyle](https://twitter.com/thejameskyle) را در توییتر دنبال کنید.***
* * *
# راه اندازی Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# پیکربندی Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***برای به روز رسانی آینده [@thejameskyle](https://twitter.com/thejameskyle) را در توییتر دنبال کنید.***
================================================
FILE: translations/fi/README.md
================================================
# Babel -käsikirja
Käsikirja koostuu kahdesta osasta:
* [Käyttäjän käsikirja](user-handbook.md) - Babel asennus/konfigurinti ja lisäohjeita.
* [Lisäosien käsikirja](plugin-handbook.md) - neuvoo lisäosien tekemisessä Babeliin.
> Jos haluat pysyä ajantasalla, seuraa Twitterissä: [@thejameskyle](https://twitter.com/thejameskyle).
Jos luet käännettyä (eli ei englanninkielistä) käsikirjaa, saatat kohdata edelleen alkuperäiskielellä kirjoitettuja kääntämättömiä osia. Jos haluat itse toimia kääntäjänä, käytä Crowdin -työkalua. Ole hyvä ja lue [vapaaehtoisten avustajien opas](/CONTRIBUTING.md) ennen osallistumista. Moni englanninkielinen termi liittyy ohjelmointiin. Jos nämä tekniset terkmit käännettäisiin suoraan muille kielille, tekstin oikeellisuus ja ymmärrettävyys kärsii. Monesti löydät suoraan alkuperäisen englanninkielisen termin ja sen lyhenteen sulkeissa `()`. Esimerkiksi: Abstract Syntax Trees (ASTs).
================================================
FILE: translations/fi/plugin-handbook.md
================================================
# Babel Plugin Handbook
Tässä ohjeessa käsitellään [Babel](https://babeljs.io) [lisäosien](https://babeljs.io/docs/advanced/plugins/) eli plugin:ien luomista.
[](http://creativecommons.org/licenses/by/4.0/)
Ohje löytyy myös muille kielille, kts. [README](/README.md) (näet koko luettelon).
# Sisällysluettelo
* [Johdanto](#toc-introduction)
* [Perusteet](#toc-basics)
* [AST-puut](#toc-asts)
* [Babel-käännöksen vaiheet](#toc-stages-of-babel)
* [Parsiminen](#toc-parse)
* [Leksikaalinen analyysi](#toc-lexical-analysis)
* [Syntaktinen analyysi](#toc-syntactic-analysis)
* [Muunnos](#toc-transform)
* [Luominen](#toc-generate)
* [Läpikäynti](#toc-traversal)
* [Vierailijat](#toc-visitors)
* [Polut](#toc-paths)
* [Polut vierailijoissa](#toc-paths-in-visitors)
* [Tila](#toc-state)
* [Scopes](#toc-scopes)
* [Sidonnat](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Määritelmät](#toc-definitions)
* [Build-mekanismit](#toc-builders)
* [Validoijat](#toc-validators)
* [Muuntajat](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Ensimmäinen Babel -lisäosasi](#toc-writing-your-first-babel-plugin)
* [Muunnosoperaatiot](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Muokkaus](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Skooppi](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Johdanto
Babel on monikäyttöinen kääntäjä JavaScriptille. Ennenkaikkea se on kokoelma moduleja joita voi käyttää monenlaiseen staattiseen analyysiin.
> Staattinen analyysi tarkoittaa koodin analysointia ilman koodin suorittamista. (Jos analyysi tehtäisiin koodin ajon aikana, kyseessä olisi dynaaminen analyysi). Staattista analyysiä voidaan tehdä monesta syystä. Analyysillä voidaan mm. syntaksitarkistaa koodia, kääntää, värjätä koodia, muuntaa koodia, optimoida, ja kompressoida sitä.
Babelin avulla voit itse rakentaa monenlaisia työkaluja jotka auttavat sekä ohjelmoinnin tuottavuudessa että ohjelmakoodin laadun parantamisessa.
> ***Jos haluat pysyä ajantasalla, seuraa Twitterissä käyttäjää: [@thejameskyle](https://twitter.com/thejameskyle).***
* * *
# Perusteet
Babel on JavaScript-kääntäjä, tarkemmin ottaen lähdekoodista-lähdekoodiin -kääntäjä, jollaisia kutsutaan usein nimellä "transpiler". Käytännössä kun siis annat Babelille pätkän JavaScript -koodia, Babel analysoi koodin, ja tuottaa uutta koodia tulosteena.
## AST-puut
Jokaisessa allaolevassa esimerkissä sinun tulee joko luoda tai käsitellä [Abstrakteja syntaksipuita](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (ts. AST:ia).
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> AST:n rakenteesta voit saada parempaa näppituntumaa tutustumalla projektiin [AST Explorer](http://astexplorer.net/). [Tässä](http://astexplorer.net/#/Z1exs6BWMq) on siihen linkki - sisältäen myös aiemmin mainitun esimerkkikoodin.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Tai JavaScriptin objektina:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Huomaa, että jokaisella AST puun tasolla on vastaava rakenne:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Huom: jotkin avain-arvoparit on poistettu esimerkin lyhentämiseksi.
Jokainen tällainen on tyyppiä **Solmu**. AST:ssä voi olla yksi ainoa solmu, tai useita satoja - tai tuhansia - solmuja. Yhdessä ne kuvaavat täysin ohjelman syntaksin, ja jo pelkästään solmujen avulla voidaan tehdä staattinen analyysi.
Jokaisella solmulla on seuraava rajapinta:
```typescript
interface Node {
type: string;
}
```
`type` -kenttä on merkkijono joka kertoo solmun tyypin, esim. `"FunctionDeclaration"`, `"Identifier"`, tai `"BinaryExpression"`). Jokaisella solmutyypillä on tarkentavia, tyyppikohtaisia lisäominaisuuksia.
Babel lisää myös jokaiseen solmuun omat ominaisuutensa, jotka tallentavat solmun esiintymiskohdan alkuperäisessä lähdekoodissa.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Ominaisuudet `start`, `end`, `loc` ovat kaikissa solmuissa.
## Babelin suorituksen vaiheet
Babelin kolme päävaihetta ovat **parsiminen**, **muuntaminen**, ja **generointi**.
### Parsiminen
**Parsimisessa** syötteenä on koodia ja tuotoksena AST. Babelin parsinta tapahtuu kahdessa vaiheessa: [**Leksikaalinen analyysi**](https://en.wikipedia.org/wiki/Lexical_analysis) ja [**Syntaksianalyysi**](https://en.wikipedia.org/wiki/Parsing).
#### Leksikaalinen analyysi
Leksikaalinen analyysi ottaa syötteenä merkkijonon koodia ja tuottaa sarjan **alkionimiä**.
Alkionimet ovat jotakuinkin yksiulotteinen taulukko kyseisen ohjelmointikielen syntaksin osasia.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Jokaisella esimerkin `tyypillä` on joukko ominaisuuksia jotka kuvaavat alkionimen:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Kuten AST:n solmuilla, näillä on myös ominaisuudet `start`, `end` ja `loc`.
#### Syntaktinen analyysi
Syntaktinen analyysi ottaa syötteenä jonon alkiosolmuja, ja tuottaa näistä AST-puun. Alkiosolmujen sisältämien kenttien perusteella ne asetetaan AST-puuhun, joka muodostaa lopulta koodin rakenteen helpommin työstettävässä muodossa.
### Muunnos
[Muunnos](https://en.wikipedia.org/wiki/Program_transformation) on vaihe, jossa AST-puu käydään läpi, ja tarpeen mukaan lisätään, päivitetään tai poistetaan puusta solmuja. Tämä on niin Babelissa kuin yleisestikin kääntäjien monimutkaisin vaihe. Lisäosat (plugins) toimivat nimenomaan muunnoksen aikana, joten tämä vaihe kuvataan kaikkein tarkimmin käsikirjassa. Siksi esittelemme muunnoksen hyvin lyhyesti tässä.
### Koodin luominen
[Koodin luonti](https://en.wikipedia.org/wiki/Code_generation_(compiler)) on vaihe jossa lopullinen AST-puu tuottaa lähdekoodia, sekä [lähdekoodikartat](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Koodin luonti on aika yksinkertaista: AST käydään läpi depth-first algoritmilla (rekursiivisesti aina lapsisolmut ensin), ja samanaikaisesti tallennetaan syntynyt koodi merkkijonoon.
## Läpikäynti
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Vierailijat
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Polut
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Polut vierailijoissa
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Tila
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Sidonnat
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Määritelmät
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Build-mekanismit
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validoijat
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Muuntajat
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Ensimmäinen Babel -lisäosasi
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Muunnosoperaatiot
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Muokkaus
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Skooppi
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/fi/user-handbook.md
================================================
# Babel User Handbook
This document covers everything you ever wanted to know about using [Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
Ohje löytyy myös muille kielille, kts. [README](/README.md) (näet koko luettelon).
# Sisällysluettelo
* [Johdanto](#toc-introduction)
* [Setting up Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Johdanto
Babel on geneerinen ja monikäyttöinen kääntäjä JavaScriptille. Babelin avulla voit käyttää (ja luoda) seuraavan sukupolven Javascript-koodia ja sitä tukevia apuohjelmia.
JavaScript uudistuu kielenä jatkuvasti, niin standardien, ehdotettujen ominaisuuksien kuin uusien ominaisuuksien toteutuksen myötä. Babel mahdollistaa uusimpien ominaisuuksien käyttöönoton vuosia ennen niiden ilmestymistä selaimiin.
Babel mahdollistaa tämän kääntämällä upouuden JavaScriptin sellaiseksi versioksi, joka toimii kaikkialla. Tällaistä käännöstä kutsutaan lähdekielestä toiseen kääntämiseksi, tai termillä 'transpiling'.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***Jos haluat pysyä ajantasalla, seuraa Twitterissä: [@thejameskyle](https://twitter.com/thejameskyle).***
* * *
# Setting up Babel
Koska JavaScript -yhteisöllä ei ole mitään "ainoaa ja oikeaa" kääntäjää, sovelluskehikkoa, alustaa, jne., Babelista on viralliset integroinnit kaikkiin suurimpiin työkaluihin. Olipa käytössäsi Gulp, Browserify, Ember, Meteor tai jotain noiden väliltä, voit odottaa silti löytäväsi virallisen integroinnin Babeliin.
Tässä perehdymme vain Babelin sisäänrakennettuihin asennusmahdollisuuksiin, mutta voit halutessasi käydä [asennusohjesivulla](http://babeljs.io/docs/setup) tutustumassa kaikkiin vaihtoehtoihin.
> **Huom!** Oppaassa oletetaan että tunnet komentorivityökalut kuten `node` ja `npm`. Tutustu näihin ennen kuin jatkat lukemista.
## `babel-cli`
Babel CLI tarjoaa yksinkertaisen tavan kääntää JS-tiedostoja komentoriviltä.
Asennetaan ensin babel-cli globaalisti.
```sh
$ npm install --global babel-cli
```
Nyt voimme kääntää kokeeksi:
```sh
$ babel my-file.js
```
Babel tulostaa käännetyn koodin terminaaliin. Jos haluat sen menevän tiedostoon, anna parametri `--out-file` tai `-o`.
```sh
$ babel example.js --out-file compiled.js
# tai
$ babel example.js -o compiled.js
```
Jos haluamme kääntää koko hakemiston sisällön uuteen hakemistoon, voimme tehdä sen parametrilla `-out-dir` tai `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Babel CLI :n käyttö projektikansiosta
Vaikka Babel CLI *voidaan* asentaa globaalisti (-g), se kannattaa asentaa **paikallisesti** aina kulloisenkin projektin yhteyteen.
Paikallisella asennuksella on kaksi etua.
1. Samalla koneella voi olla projekteja, jotka haluavat tietyn Babel -version, jolloin voit päivittää niitä erikseen (paikallisesti).
2. Projektisi ei enää riipu omasta työympäristöstäsi. Se tekee projektistasi helpommin siirrettävän ja helpomman asentaa.
Voimme asentaa Babel CLI paikallisesti antamalla:
```sh
$ npm install --save-dev babel-cli
```
> **Huom:** Koska Babelin suoritus koneen globaalista versiosta on huono idea, voit varmuuden vuoksi poistaa globaalin asennuksen:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***Jos haluat pysyä ajantasalla, seuraa Twitterissä: [@thejameskyle](https://twitter.com/thejameskyle).***
================================================
FILE: translations/fr/README.md
================================================
# Manuel de Babel
Ce manuel est divisé en deux parties :
* [Manuel utilisateur](user-handbook.md) - Comment démarrer/configurer Babel et plus encore.
* [Manuel des plugins](plugin-handbook.md) - Comment créer des plugins pour Babel.
> Pour les prochaines mises à jour, suivez [@thejameskyle](https://twitter.com/thejameskyle) sur Twitter.
Si vous êtes en train de lire une traduction de ce manuel, vous allez sûrement trouver des sections qui ne sont pas encore traduites. Si vous voulez contribuer à la traduction, vous pouvez le faire à l'aide du site Crowdin. Veuillez lire le [guide de contribution](/CONTRIBUTING.md) pour plus d'informations. Vous trouverez des mots anglais ou anglicisme qui sont des concepts de programmation. Ces concepts risque de perdre leurs sens une fois traduits dans une autre langue. Dans de nombreux cas vous trouverez une traduction littérale suivi du terme en anglais entre parenthèses `()`. Par exemple : Arbres de Syntaxe abstraite (ASTs).
================================================
FILE: translations/fr/plugin-handbook.md
================================================
# Manuel du plugin de Babel
Ce présent document décrit les méthodes de création des [plugins](https://babeljs.io/docs/advanced/plugins/) pour [Babel](https://babeljs.io).
[](http://creativecommons.org/licenses/by/4.0/)
Ce manuel est disponible dans d'autres langues, consulter le [README](/README.md) pour obtenir la liste complète.
# Sommaire
* [Introduction](#toc-introduction)
* [Notions de base](#toc-basics)
* [AST](#toc-asts)
* [Les étapes de Babel](#toc-stages-of-babel)
* [Analyse](#toc-parse)
* [Analyse lexicale](#toc-lexical-analysis)
* [Analyse syntaxique](#toc-syntactic-analysis)
* [Transformation](#toc-transform)
* [Génération](#toc-generate)
* [Parcours](#toc-traversal)
* [Visiteurs](#toc-visitors)
* [Chemins](#toc-paths)
* [Chemins dans les visiteurs](#toc-paths-in-visitors)
* [État](#toc-state)
* [Portées](#toc-scopes)
* [Liaisons](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Définitions](#toc-definitions)
* [Constructeurs](#toc-builders)
* [Validateurs](#toc-validators)
* [Convertisseurs](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Écriture de votre premier plugin de Babel](#toc-writing-your-first-babel-plugin)
* [Opérations de transformations](#toc-transformation-operations)
* [Visite](#toc-visiting)
* [Récupération du chemin du sous-nœud](#toc-get-the-path-of-a-sub-node)
* [Vérification si un nœud est un certain type](#toc-check-if-a-node-is-a-certain-type)
* [Vérification si un chemin est un certain type](#toc-check-if-a-path-is-a-certain-type)
* [Vérification si un identificateur est référencé](#toc-check-if-an-identifier-is-referenced)
* [Trouver un chemin d'un parent spécifique](#toc-find-a-specific-parent-path)
* [Récupérer les chemins des frères](#toc-get-sibling-paths)
* [Arrêt du parcours](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Remplacement d’un nœud](#toc-replacing-a-node)
* [Remplacement d’un nœud par plusieurs nœuds](#toc-replacing-a-node-with-multiple-nodes)
* [Remplacement d’un nœud avec une chaîne source](#toc-replacing-a-node-with-a-source-string)
* [Insertion d'un nœud enfant](#toc-inserting-a-sibling-node)
* [Insertion dans un conteneur](#toc-inserting-into-a-container)
* [Suppression d'un nœud](#toc-removing-a-node)
* [Remplacement d'un parent](#toc-replacing-a-parent)
* [Suppression d'un parent](#toc-removing-a-parent)
* [Portée](#toc-scope)
* [Vérification si une variable locale est liée](#toc-checking-if-a-local-variable-is-bound)
* [Génération d'un UID](#toc-generating-a-uid)
* [Poussée d'une déclaration de variable vers un scope parent](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Renommage d'une liaison et de ses références](#toc-rename-a-binding-and-its-references)
* [Options du plugin](#toc-plugin-options)
* [Pré et Post dans les plugins](#toc-pre-and-post-in-plugins)
* [Activation de la syntaxe dans les plugins](#toc-enabling-syntax-in-plugins)
* [Nœuds de création](#toc-building-nodes)
* [Meilleures pratiques](#toc-best-practices)
* [Éviter de traverser l'AST autant que possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Fusionner les visiteurs quand c'est possible](#toc-merge-visitors-whenever-possible)
* [N'utilisez pas "traverse" lorsqu'une lecture manuelle est possible](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimisation des visiteurs imbriqués](#toc-optimizing-nested-visitors)
* [Etre conscient des structures imbriqués](#toc-being-aware-of-nested-structures)
* [Tests unitaires](#toc-unit-testing)
# Introduction
Babel est un compilateur multifonction générique pour JavaScript. Plus que cela, c'est une collection de modules qui peut être utilisée pour plusieurs formes différentes d'analyse statique.
> L'analyse statique est le processus d'analyse du code sans l'exécuter. (L'analyse du code lors de l'exécution est appelée analyse dynamique). Le but de l'analyse statique varie grandement. Il peut être utilisé pour le "linting", la compilation, le "highlighting", la transformation du code, l'optimisation, la minification et bien plus.
Vous pouvez utiliser Babel pour construire différents types d'outils qui peuvent vous aider à être plus productifs et écrire de meilleurs logiciels.
> ***Pour les prochaines mises à jour, suivez [@thejameskyle](https://twitter.com/thejameskyle) sur Twitter.***
* * *
# Notions de base
Babel est un compilateur JavaScript, plus précisément un compilateur de code source en un autre code source, souvent appelé un « transpiler ». Cela signifie que vous donnez à Babel du code JavaScript, Babel modifie le code et génère un nouveau code en sortie.
## AST
Chacune de ces étapes implique la création ou le travail avec une [Arbre de syntaxe abstraite](https://en.wikipedia.org/wiki/Abstract_syntax_tree) ou AST.
> Babel utilise un AST modifié de [ESTree](https://github.com/estree/estree), avec la spécification principale située [ici](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Découvrez [AST Explorer](http://astexplorer.net/) pour avoir une meilleure idée des nœuds AST. Voici [un lien](http://astexplorer.net/#/Z1exs6BWMq) avec l'exemple de code ci-dessus.
Ce même programme peut être représenté sous forme d'une arborescence comme celle-ci :
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Ou comme un objet JavaScript comme suit :
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Vous remarquerez que chaque niveau de l'AST a une structure similaire :
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Remarque : Certaines propriétés ont été supprimées par souci de simplicité.
Chacun de ces niveaux est connu sous le terme **Nœud**. L'AST peut être composé d'un seul nœud, de centaines ou même de milliers de nœuds. Ensemble, ils sont capables de décrire la syntaxe d'un programme qui peut être utilisée pour l'analyse statique.
Chaque Nœud a cette interface :
```typescript
interface Node {
type: string;
}
```
Le champ `type` est une chaîne qui représente le type de l'objet nœud (c'est à dire. `"FunctionDeclaration"`, `"Identifier"` ou `"BinaryExpression"`). Chaque type de nœud définit un ensemble de propriétés supplémentaires qui décrivent ce type de nœud particulier .
Des propriétés supplémentaires sont présentes sur chaque nœud que Babel génère qui décrivent la position du nœud dans le code source original.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Ces propriétés `start`, `end`, `loc`, apparaissent sur chacun des nœuds.
## Les étapes de Babel
Les trois étapes principales de Babel sont **analyse**, **transformation**, **génération**.
### Analyse
L'étape **d'analyse** prend le code et génère un AST. Il y a deux phases d'analyse dans Babel : [**Analyse lexicale**](https://en.wikipedia.org/wiki/Lexical_analysis) et [**Analyse Syntaxique**](https://en.wikipedia.org/wiki/Parsing).
#### Analyse lexicale
L'analyse lexicale prendra une chaîne de code et la transformera en un flux de **jetons** (tokens).
Vous pouvez considérer les jetons comme un tableau plat composé de morceaux de syntaxe de langage.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Ici, chacun des `type`s a un ensemble de propriétés décrivant le jeton :
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Comme les nœuds AST, ils ont aussi un `start`, `end` et `loc`.
#### Analyse syntaxique
L'analyse syntaxique prendra un flux de jetons et le transformera en une représentation AST. En utilisant les informations dans les jetons, cette phase les reformatera en un AST qui représente la structure du code, cela permettra de les manipuler plus facilement.
### Transformation
L’étape de [transformation](https://en.wikipedia.org/wiki/Program_transformation) prend un AST, le parcourt, en ajoutant, mettant à jour et supprimant des nœuds au fur et à mesure. C’est de loin la partie la plus complexe de Babel ou de n’importe quel compilateur. Il s’agit du fonctionnement des plugins et donc cela sera l’objet de la majeure partie de ce manuel. Donc, pour l'instant, nous ne l'étudierons pas trop profondément.
### Génération
L’étape de [génération de code](https://en.wikipedia.org/wiki/Code_generation_(compiler)) prend l’AST final et il le retourne en une chaîne de code, en créant les [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
La génération du code est assez simple : on traverse l’AST en profondeur, en créant une chaîne qui représente le code transformé.
## Parcours
Lorsque vous souhaitez transformer un AST, vous devez [parcourir l’arborescence](https://en.wikipedia.org/wiki/Tree_traversal) de manière récursive.
Supposons que nous avons le type `FunctionDeclaration`. Il possède quelques propriétés : `id`, `params` et `body`. Elles ont chacune des nœuds imbriqués.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Nous partons donc de `FunctionDeclaration` et dans le but de connaitre ses propriétés internes nous visitons les nœuds ainsi que leurs enfants.
Ensuite, nous allons sur `id` qui est un `Identifier`. Les `Identifier`s n'ont pas de propriétés de noeud d'enfant donc nous repartons.
Après, il y a `params`, qui est un tableau de nœuds que nous visitons les uns après les autres. Dans ce cas, c’est un seul nœud, qui est également un `Identifier` donc nous repartons.
Puis nous arrivons sur `corps` qui est un `BlockStatement` avec une propriété `body` qui est un tableau de nœuds que nous visitons les uns après les autres.
Le seul élément ici est un nœud de `ReturnStatement` qui possède un `argument`, nous allons dans `argument` et nous trouvons un `BinaryExpression`.
Le `BinaryExpression` a un `operator`, un `left` et un `right`. L’operator n’est pas un nœud, juste une valeur, alors nous n’y allons pas, et au lieu de cela, nous visitons juste `left` et `right`.
Ce parcours s’effectue tout au long de l’étape de transformation de Babel.
### Visiteurs
Lorsque nous parlons "d'aller" vers un nœud, nous voulons dire que nous sommes en train de le **visiter**. Nous utilisons ce terme car il y a cette notion de [**visiteur**](https://en.wikipedia.org/wiki/Visitor_pattern).
Les visiteurs sont un modèle utilisé dans le parcours de l’AST des langages. Plus simplement, c'est un objet avec des méthodes définies pour accepter les types de nœud particulier dans une arborescence. C’est un peu abstrait, alors prenons un exemple.
```js
const MyVisitor = {
Identifier() {
console.log("Appelé !");
}
};
// Vous pouvez aussi créer un visiteur et lui ajouter des méthodes plus tard
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Remarque :** `Identifier() { ... }` est le raccourci de `identificateur: {enter() { ... }}`.
Il s’agit d’un visiteur basique qui est utilisé lors d'un parcours, il appellera la méthode `Identifier()` à chaque `Identifier` dans l’arborescence.
Donc avec ce code, la méthode `Identifier()` sera appelée quatre fois pour chaque `Identifier` (y compris `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
Ces appels sont tous faits lorsqu'on **entre** dans le nœud. Cependant, il est possible d’appeler une méthode de visiteur lorsqu'on **sort**.
Imaginons que nous avons cette arborescence :
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
Comme nous parcourons chaque branche de l’arborescence vers le bas, nous tombons finalement sur des impasses que nous devons remonter pour obtenir le nœud suivant. En descendant l’arborescence, nous **entrons** dans chaque nœud, puis en remontant nous **sortons** de chaque nœud.
*Suivons* le processus de l’arborescence ci-dessus.
* Entre dans `FunctionDeclaration`
* Entre dans `Identifier (id)`
* Arrive à une impasse
* Sort de `Identifier (id)`
* Entre dans `Identifier (params[0])`
* Arrive à une impasse
* Sort de `Identifier (params[0])`
* Entre dans `BlockStatement (body)`
* Entre dans `ReturnStatement (body)`
* Entre dans `BinaryExpression (argument)`
* Entre dans `Identifier (left)`
* Arrive à une impasse
* Sort de `Identifier (left)`
* Entre dans `Identifier (right)`
* Arrive à une impasse
* Sort de `Identifier (right)`
* Sort de `BinaryExpression (argument)`
* Sort de `ReturnStatement (body)`
* Sort de `BlockStatement (body)`
* Sort de `FunctionDeclaration`
Lorsque vous créez un visiteur, vous avez deux possibilités pour visiter un nœud.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
Si nécessaire, vous pouvez également appliquer la même fonction pour plusieurs nœuds visiteur en les séparant avec un `|` dans le nom de la méthode sous forme de chaîne comme `Identifier|MemberExpression`.
Exemple d’utilisation dans le plugin de [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47)
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
Vous pouvez également utiliser des alias comme nœuds visiteur (tel que défini dans [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
Par exemple,
`Function` est un alias pour `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` et `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Chemins
Un AST a généralement beaucoup de nœuds, mais comment les nœuds sont liés les uns aux autres ? Nous pourrions avoir un objet mutable géant qui les manipule et a un accès totale, ou nous pouvons simplifier ceci avec les **chemins**.
Un **chemin** est un objet qui représente le lien entre deux nœuds.
Par exemple, si nous prenons le nœud suivant et son enfant :
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
Et qu'on représente l’enfant `Identifier` comme un chemin, il ressemble donc à ceci :
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
Il a également des métadonnées supplémentaires à propos du chemin :
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
Ainsi que des tonnes et des tonnes de méthodes liées à l’ajout, la mise à jour, le déplacement et la suppression de nœuds, mais nous en parlerons plus tard.
D'une certaine manière, les chemins sont une représentation **réactive** de la position d'un nœud dans l'arborescence et ont toutes sortes d'informations sur le nœud. Chaque fois que vous appelez une méthode qui modifie l’arborescence, cette information est mise à jour. Babel gère tout cela pour vous permettre de travailler avec les nœuds de manière simple et sans toucher le plus possible aux états dans lequel ils sont.
#### Chemins dans les visiteurs
Lorsque vous avez un visiteur qui a une méthode `Identifier()`, vous visitez en fait le chemin à la place du nœud. De cette façon vous travaillez principalement avec la représentation réactive d’un nœud au lieu du nœud lui-même.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### État
L'état est l'ennemi de la transformation de l'AST. L'état vous jouera des tours à maintes reprises et vos hypothèses à propos de l'état seront presque toujours démenties par une syntaxe que vous n'avez pas envisagé.
Prenons le code suivant :
```js
function square(n) {
return n * n;
}
```
Nous allons écrire un rapide visiteur pas très originale qui renommera `n` en `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
Cela pourrait fonctionner pour le code ci-dessus, mais nous pouvons facilement le casser en procédant ainsi :
```js
function square(n) {
return n * n;
}
n;
```
La meilleure façon de le traiter, c'est la récursivité. Nous allons donc faire comme dans un film de Christopher Nolan et mettre un visiteur à l’intérieur d’un visiteur.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Bien sûr, il s’agit d’un exemple artificiel mais il montre comment éliminer l’état global de vos visiteurs.
### Portées
Ensuite, nous allons introduire le concept d’une [**portée**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript a une [portée lexicale](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), qui est une structure arborescente où les blocs créent une nouvelle portée.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Dès que vous créez une référence en JavaScript, que ce soit par une variable, une fonction, une classe, un paramètre, un import, une étiquette, etc., elle appartient à la portée actuelle.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "Je suis la portée créée par `scopeOne()`";
function scopeTwo() {
var two = "Je suis la portée créée par `scopeTwo()`";
}
}
```
Le code dans une portée plus profonde peut utiliser une référence d’une portée plus haute.
```js
function scopeOne() {
var one = "Je suis la portée créée par `scopeOne()`";
function scopeTwo() {
one = "Je mets à jour la référence dans `scopeOne` à l'intérieur de `scopeTwo`";
}
}
```
Une portée plus basse pourrait également créer une référence du même nom sans la modifier.
```js
function scopeOne() {
var one = "Je suis dans la portée créée par `scopeOne()`";
function scopeTwo() {
var one = "Je crée un nouveau `one` mais en laissant la référence `scopeOne()` seule.";
}
}
```
Lors de l'écriture d'une transformation, nous devons nous méfier de la portée. Il faut s’assurer que nous ne cassons pas le code existant tout en modifiant les différentes parties de celui-ci.
On peut vouloir ajouter de nouvelles références et s'assurer qu’elles n’entrent pas en conflit avec celles qui existent déjà. Ou peut-être que nous voulons juste trouver où est référencée une variable. Nous voulons être en mesure de suivre ces références dans une portée donnée.
Une portée peut être représentée ainsi :
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
Lorsque vous créez une nouvelle portée, vous devez donc le faire en lui donnant un chemin et une portée mère. Puis au cours du processus de parcours, il faut recueillir toutes les références (les « liaisons ») dans cette portée.
Une fois cela fait, il y a toutes sortes de méthodes que vous pouvez utiliser sur des portées. Nous en parlerons cependant plus tard.
#### Liaisons
Les références appartiennent toutes à une portée particulière, cette relation est appelée une **liaison**.
```js
function scopeOnce() {
var ref = "Ceci est une liaison";
ref; // Ceci est une référence à un liaison
function scopeTwo() {
ref; // Ceci est une référence à une liaison à partir d'une portée inférieure
}
}
```
Une liaison unique ressemble à ceci :
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
Avec cette information, vous pouvez trouver toutes les références qui ont un liaison, voir quel est le type de liaison (paramètre, déclaration, etc.), rechercher à quelle portée elle appartient ou obtenir une copie de son identificateur. Vous pouvez même dire si elle est constante ou pas, regardez quels chemins sont à l’origine qu'elle soit une constante ou pas.
Être capable de dire si une liaison est constante est utile pour beaucoup de choses, dont la plus importante, c'est la minification.
```js
function scopeOne() {
var ref1 = "Ceci est une liaison constante";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "Ceci *n'est pas* une liaison constante";
ref2 = "Car la valeur est modifiée";
}
}
```
* * *
# API
Babel est en fait une collection de modules. Dans cette section, nous verrons les plus importants, en expliquant ce qu’ils font et comment les utiliser.
> Remarque : Ceci ne remplace pas la documentation détaillée de l'API qui sera disponible dans peu de temps.
## [`babylon`](https://github.com/babel/babylon)
Babylone est l'analyseur de Babel. Il est démarré comme un fork de Acorn, c' est rapide, simple à utiliser, il a une architecture de plugin pour les fonctionnalités non standards (ainsi que les futures standards).
Tout d’abord, nous devons l’installer.
```sh
$ npm install --save babylon
```
Commençons par analyser simplement une chaîne de code :
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
Nous pouvons aussi passer des options à `parse()` comme suit :
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` peut avoir soit `"module"` ou `"script"` qui est le mode que doit utiliser Babylon pour analyser. `"module"` analysera en mode strict et permettra les déclarations de module, ce ne sera pas le cas de `"script"`.
> **Remarque :** `sourceType` a par défaut `"script"` et partira en erreur lorsqu’il détecte `import` ou `export`. Passez `sourceType: « module »` pour se débarrasser de ces erreurs.
Comme Babylon est construit avec une architecture de plugin, il y a également une option `plugins` qui activera les plugins internes. Remarquez que Babylon n’a pas encore ouvert cette API aux plugins externes, bien qu'il puisse le faire dans le futur.
Pour voir une liste complète des plugins, consultez le [README de Babylon](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
Le module Traverse de Babel maintient l’état général de l’arborescence et est responsable du remplacement, de la suppression et de l’ajout de nœuds.
Installez-le en exécutant :
```sh
$ npm install --save babel-traverse
```
Nous pouvons l’utiliser avec Babylon pour parcourir et mettre à jour les nœuds :
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types est une bibliothèque d’utilitaires de type Lodash pour les nœuds de l’AST. Il contient des méthodes pour la construction, la validation et la conversion des nœuds de l’AST. C'est pratique pour le nettoyage logique de l'AST avec des méthodes utiles bien pensées.
Vous pouvez l’installer en exécutant :
```sh
$ npm install --save babel-types
```
Puis commencez à l’utiliser :
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Définitions
Babel Types a des définitions pour chaque type de nœud, avec des informations sur les propriétés qu'il contient, quelles sont les valeurs valides, comment construire ce nœud, comment le nœud doit être parcouru et l'alias du nœud.
Une définition de type de nœud ressemble à ceci :
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Constructeurs
Vous remarquerez que la définition ci-dessus pour `BinaryExpression` a un champ `builder`.
```js
builder: ["operator", "left", "right"]
```
C’est parce que chaque type de nœud obtient une méthode de construction, qui, une fois utilisé ressemble à ceci :
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Ce qui crée un AST comme ceci :
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Qui, une fois affiché ressemble à ceci :
```js
a * b
```
Les constructeurs valideront également les nœuds qu’ils créent et lèveront des erreurs descriptives s'ils sont mal utilisés. Ce qui mène au prochain type de méthode.
### Validateurs
La définition de `BinaryExpression` contient aussi des informations sur les `fields` (champs) d’un nœud et comment les valider.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
Ceci est utilisé pour créer deux types de validation de méthodes. La première étant `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
Ce test pour s'assurer que le nœud est une expression binaire, mais vous pouvez également passer un deuxième paramètre pour vous assurer que le nœud contient certaines propriétés et valeurs.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
Il y a aussi une version plus assertive de ces méthodes, *ehem*, qui déclenchera des erreurs au lieu de retourner la valeur `true` ou `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Convertisseurs
> \[WIP\] (Travail en cours)
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator est le générateur de code pour Babel. Il prend un AST et le transforme en code avec des sourcemaps.
Exécutez la commande suivante pour l’installer :
```sh
$ npm install --save babel-generator
```
Puis utilisez-le
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
Vous pouvez également passer des options à `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template est un autre module minuscule mais incroyablement utile. Il vous permet d'écrire des chaînes de code avec des emplacements que vous pouvez utiliser au lieu de construire manuellement un AST massif. En informatique, cette fonctionnalité est appelée quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Écriture de votre premier plugin de Babel
Maintenant que vous êtes familiarisé avec les bases de Babel, nous allons les attacher ensemble avec l'API de plugin.
Commencez par une `function` qui passe l’objet courant de [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core).
```js
export default function(babel) {
// contenu du plugin
}
```
Étant donné que vous allez l’utiliser souvent, vous aurez probablement envie de saisir simplement ` babel.types` comme ceci :
```js
export default function({ types: t }) {
// contenu du plugin
}
```
Puis vous retournez un objet avec une propriété `visitor` qui est le visiteur primaire du plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// contenu du visiteur
}
};
};
```
Chaque fonction dans le visiteur reçoit 2 arguments : `path` et `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Nous allons écrire un plugin rapide pour montrer comment il fonctionne. Voici notre code source :
```js
foo === bar;
```
Ou sous forme d'un AST :
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
Nous allons commencer en ajoutant une méthode visiteur `BinaryExpression`.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Ensuite, nous allons restreindre `BinaryExpression` pour qu'il utilise uniquement l'opérateur `===`.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Maintenant nous allons remplacer la propriété `left` avec un nouvel identificateur :
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Déjà si nous exécutons ce plugin que nous obtiendrions :
```js
sebmck === bar;
```
Maintenant, nous allons remplacer la propriété `right`.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
Et voici notre résultat final :
```js
sebmck === dork;
```
Génial ! Notre tout premier plugin Babel.
* * *
# Opérations de transformations
## Visite
### Récupération du chemin du sous-nœud
Pour accéder à la propriété d’un nœud de l’AST, vous accéder normalement au nœud, puis à la propriété. `Path.Node.Property`
```js
// le nœud AST BinaryExpression possède des propriétés : `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
Si au lieu de cela, vous avez besoin d'accéder au `path` de cette propriété, utilisez la méthode `get` d’un chemin, en passant la chaîne de la propriété.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Vérification si un nœud est un certain type
Si vous voulez vérifier le type d'un noeud, la meilleure façon de procéder est la suivante :
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
Vous pouvez également faire une vérification peu profonde pour les propriétés de ce nœud :
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
C’est fonctionnellement équivalent à :
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Vérification si un chemin est un certain type
Un chemin a les mêmes méthodes pour contrôler le type d’un nœud :
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
équivaut à faire :
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Vérification si un identificateur est référencé
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Ou une autre alternative :
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Trouver un chemin d'un parent spécifique
Parfois, vous aurez besoin de remonter l’arborescence d’un chemin jusqu'à ce qu’une condition soit satisfaite.
Appelez le `callback` fourni avec les `NodePath`s de tous les parents. Lorsque le `callback` renvoie une valeur truthy, nous retournons ce `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
Si le chemin actuel doit être inclus :
```js
path.find((path) => path.isObjectExpression());
```
Trouver le programme ou la fonction parent le plus proche :
```js
path.getFunctionParent();
```
Remonter l’arborescence jusqu'à ce que nous accédions à un chemin du nœud parent dans une liste
```js
path.getStatementParent();
```
### Récupérer les chemins des frères
Si un chemin est une liste, comme pour le corps de `Function` ou `Program`, il aura des « frères ».
* Vérifiez si un chemin fait partie d’une liste avec `path.inList`
* Vous pouvez obtenir les frère environnants avec `path.getSibling(index)`,
* L'index du chemin dans le conteneur avec `path.key`,
* Le conteneur du chemin (un tableau de tous les nœuds frères) avec `path.container`
* Obtenez le nom de la clé du conteneur de liste avec `path.listKey`
> Ces API sont utilisées dans le plugin [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) utilisé dans [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Arrêt du parcours
Si votre plugin ne doit pas fonctionner dans certaine situation, la chose la plus simple à faire, c'est d’écrire un retour anticipé.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
Si vous sous-parcourez un chemin de niveau supérieur, vous pouvez utiliser 2 méthodes fournies de l’API :
`path.skip()` saute le parcours du fils du chemin courant. `path.stop()` arrête de parcourir entièrement.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // si la vérification des enfants n'est pas pertinent
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // si vous souhaitez enregistrer un état puis arrêter le parcours
}
});
```
## Manipulation
### Remplacement d’un nœud
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Remplacement d’un nœud par plusieurs nœuds
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Remarque :** Lors du remplacement d’une expression avec plusieurs nœuds, ils doivent être sous forme d'instruction. C’est parce que Babel utilise abondamment des heuristiques lors du remplacement des nœuds, ce qui signifie que vous pouvez faire quelques transformations assez folles qui seraient autrement très verbeuses.
### Remplacement d’un nœud avec une chaîne source
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Remarque :** Il n'est pas recommandé d'utiliser cette API, à moins que votre source de données soit une chaîne de caractère dynamique. Dans un cas nominal, il est plus efficace d'analyser le code à l'extérieur du visiteur.
### Insertion d'un nœud enfant
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Remarque :** Cela doit toujours être une instruction ou un tableau d’instructions. Cet exemple utilise les mêmes heuristiques que ceux mentionnés dans [Remplacement d'un nœud par plusieurs nœuds](#replacing-a-node-with-multiple-nodes).
### Insertion dans un conteneur
Si vous voulez insérer dans un nœud AST une propriété qui est un tableau comme `body`. C'est similaire à `insertBefore` et `insertAfter` sauf que vous devez spécifier `listKey` qui est généralement `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Suppression d'un nœud
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Remplacement d'un parent
Il suffit d’appeler `replaceWith` avec le parentPath : `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Suppression d'un parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Portée
### Vérification si une variable locale est liée
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
Ceci traversera l’arborescence de la portée et vérifiera la liaison particulière.
Vous pouvez également vérifier si une portée a sa **propre** liaison :
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Génération d'un UID
Cela générera un identificateur qui n’empiète pas sur les variables définies localement.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Poussée d'une déclaration de variable vers un scope parent
Parfois, vous voudrez peut-être pousser une `VariableDeclaration` pour pouvoir l'assigner.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Renommage d'une liaison et de ses références
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternativement, vous pouvez renommer une liaison vers un identificateur unique généré :
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Options du plugin
Si vous souhaitez permettre à vos utilisateurs de personnaliser le comportement de votre plugin Babel, vous pouvez accepter des options de plugin spécifiques que les utilisateurs peuvent définir de cette façon :
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
Ces options sont alors passées dans les visiteurs du plugin à travers l'objet `state` :
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
Ces options sont spécifiques au plugin et vous ne pouvez pas accéder aux options des autres plugins.
## Pré et Post dans les plugins
Les plugins peuvent avoir des fonctions qui sont exécutées avant ou après les plugins. Elles peuvent être utilisées à des fins d’installation ou de nettoyage/analyse.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Activation de la syntaxe dans les plugins
Les plugins peuvent activer [babylon plugins](https://github.com/babel/babylon#plugins) pour que les utilisateurs n'aient pas besoin de les installer/activer. Cela empêche une erreur d'analyse sans hériter du plugin de syntaxe.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Lancer une erreur de syntaxe
Si vous souhaitez lever une erreur avec babel-code-frame et un message :
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
L’erreur ressemblera à :
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Nœuds de création
Lors de l’écriture des transformations, vous aurez souvent envie de créer certains nœuds pour les insérer dans l’AST. Comme mentionné précédemment, vous pouvez le faire en utilisant les méthodes du [builder](#builders) dans le package [`babel-types`](#babel-types).
Le nom de la méthode pour un constructeur (builder) est simplement le nom du type de nœud que vous voulez construire sauf que la première lettre est en minuscule. Par exemple si vous voulez construire un `MemberExpression` vous utiliserez `t.memberExpression(...)`.
Les arguments de ces constructeurs sont déterminés par la définition du nœud. Il y a un peu de travail à faire pour générer la documentation afin der lire facilement les définitions, mais en attendant, elles peuvent tous être trouvés [ici](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
Une définition de nœud ressemble à ce qui suit :
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Ici vous pouvez voir toutes les informations sur ce type de nœud particulier, y compris comment le construire, le parcourir et le valider.
En examinant la propriété `builder`, vous pouvez voir les 3 arguments qui seront nécessaires pour appeler la méthode du constructeur (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Veuillez notez que parfois il y a plus de propriétés personnalisables sur le nœud que celles contenues dans le tableau `builder` . Cela doit empêcher le constructeur d'avoir trop d'arguments. Dans ces cas, vous devrez définir manuellement les propriétés. Un exemple de ceci, c'est [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Exemple
// parce que le builder ne contient pas la propriété `async`
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
Vous pouvez voir la validation pour les arguments du constructeur avec l’objet `fields`.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
Vous pouvez voir que `object` doit être une `Expression`, `property` doit être soit une `Expression` ou un `Identifier` selon si l’expression membre est `computed` ou non, `computed` est simplement une valeur booléenne qui est par défaut à `false`.
Donc nous pouvons construire un `MemberExpression` en procédant comme suit :
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` est facultatif
);
```
Qui se traduira par :
```js
object.property
```
Cependant, nous avons dit que `object` doit être une `Expression` alors pourquoi `Identifier` est valide ?
Eh bien, si nous regardons la définition de `Identifier`, nous pouvons voir qu’il a une propriété `aliases` qui stipule que c’est aussi une expression.
```js
aliases: ["Expression", "LVal"],
```
Donc puisque `MemberExpression` est un type de `Expression`, nous pourrions le définir comme `object` d’un autre `MemberExpression` :
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Qui se traduira par :
```js
member.expression.property
```
Il est très improbable que vous appreniez par cœur les signatures de méthode du constructeur pour chaque type de nœud. Donc vous devriez prendre du temps et comprendre comment ils sont générés depuis les définitions du nœud.
Vous pouvez trouver toutes les [définitions actuelles ici](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) et vous pouvez les voir [documentées ici](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Meilleures pratiques
## Créer des constructeurs d'assistance et des vérificateurs
Il est assez simple d'extraire certaines vérifications (si un nœud est un certain type) dans leurs propres fonctions d'aide, ainsi que d'extraire des aides pour des types de nœuds spécifiques.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Éviter de traverser l'AST autant que possible
Parcourir l'AST est coûteux, et il est facile de parcourir accidentellement l'AST plus que nécessaire. Cela pourrait être la source de milliers, voire de dizaines de milliers d'opérations supplémentaires.
Babel optimise ces parcours le plus possible, en fusionnant les visiteurs ensemble si possible afin de tout faire en un seul parcours.
### Fusionner les visiteurs quand c'est possible
Lorsque vous écrivez des visiteurs, il peut être tentant d’appeler `path.traverse` à plusieurs endroits où ils sont logiquement nécessaires.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
Cependant, il est bien plus efficace d'écrire un seul visiteur qui n'est appelé qu'une seule fois. Dans le cas contraire, vous visiteriez le même arbre plusieurs fois sans raison.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### N'utilisez pas "traverse" lorsqu'une lecture manuelle est possible
Il peut être tentant d'appeler `path.traverse` pour rechercher un type de nœud particulier.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
Cependant, lorsque vous recherchez quelque chose de précis et peu profond dans l'arbre, il est souvent possible de récupérer manuellement des nœuds fils sans effectuer une traversée coûteuse.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimisation des visiteurs imbriqués
Lorsque vous imbriquez un visiteur dans un autre, il paraît sensé de les imbriquer également dans votre code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
Cependant, ceci crée un nouvel objet visiteur chaque fois que `FunctionDeclaration()` est appelée. Cela peut être coûteux, car Babel effectue un traitement chaque fois qu'un nouvel objet visiteur est transmis (comme l'explosion de clés contenant plusieurs types, la validation et l'ajustement de la structure de l'objet). Parce que Babel stocke des flags sur les objets visiteurs indiquant qu'il a déjà effectué ce traitement, il est préférable de stocker le visiteur dans une variable et de transmettre le même objet à chaque fois.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
Si vous avez besoin d'enregistrer un état à l'intérieur d'un visiteur imbriqué, comme ceci :
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
Vous pouvez le fournir en paramètre de la méthode `traverse()`, puis y accéder grâce à l'opérateur `this` au sein du visiteur.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Etre conscient des structures imbriqués
Quelquefois en pensant à une transformation donnée, vous pourriez oublier que la structure de donnée peut être imbriquée.
Par exemple, imaginez que nous voulons rechercher la `ClassMethod` du `constructor` depuis la la `ClassDeclaration` de `Foo`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
Nous ignorons le fait que les classes peuvent être imbriquées et en utilisant le parcours ci-dessus nous tomberons sur un `constructor` imbriqué :
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Tests unitaires
Il existe quelques méthodes principales pour tester les plugins babel : les tests instantanés, les tests AST et les tests d'exécution. Nous utiliserons [jest](http://facebook.github.io/jest/) pour cet exemple car il prend en charge le test instantané dès la sortie de la boîte. L'exemple que nous créons ici est hébergé dans [ce dépôt](https://github.com/brigand/babel-plugin-testing-example).
Nous avons d'abord besoin d'un plugin babel, nous allons le mettre dans src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Tests instantanés
Ensuite, installez nos dépendances avec `npm install --save-dev babel-core jest`, et nous pourrons commencer à écrire notre premier test : l'instantané. Les tests d'instantanés nous permettent d'inspecter visuellement la sortie de notre plugin babel. Nous lui donnons une entrée, puis nous lui disons de faire un instantané, et de l'enregistrer dans un fichier. Nous vérifions les instantanés dans git. Cela nous permet de voir quand nous avons affecté la sortie de l'un de nos cas de test. Cela donne aussi un diff dans les pull requests. Bien sûr, vous pouvez le faire avec n'importe quel framework de test, mais avec jest la mise à jour des instantanés est assez simple avec `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
Cela nous donne un fichier instantané dans `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
Si nous changeons 'bar' en 'baz' dans notre plugin et que nous recommençons l'exécution de jest, nous obtenons ceci :
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
Nous voyons comment notre modification du code du plugin a affecté la sortie de notre plugin, et si la sortie nous semble bonne, nous pouvons lancer `jest -u` pour mettre à jour l'instantané.
### Tests AST
En plus des tests d'instantanés, nous pouvons inspecter manuellement l'AST. C'est un exemple simple mais fragile. Pour les situations plus complexes, vous pouvez utiliser babel-traverse. Il vous permet de spécifier un objet avec une clé `visitor`, exactement comme vous l'utilisez pour le plugin lui-même.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// ou babelTraverse(program, {visitor: ...})
});
```
### Tests d'exécution
Ici, nous allons transformer le code, puis évaluer s'il se comporte correctement. Notez que nous n'utilisons pas `assert` dans le test. Cela garantit que si notre plugin fait des choses bizarres comme la suppression de la ligne d'assertion par accident, le test échouera toujours.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test que foo a été renommé en baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Le cœur de Babel utilise une [approche similaire](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) pour les tests instantanés et d’exécutions.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
Ce paquet facilite les tests de plugins. Si vous connaissez le [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) d'ESLint, cela devrait vous être familier. Vous pouvez regarder [les docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) pour avoir une idée complète de ce qui est possible, mais voici un exemple simple :
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***Pour les prochaines mises à jour, suivez [@thejameskyle](https://twitter.com/thejameskyle) et [@babeljs](https://twitter.com/babeljs) sur Twitter.***
================================================
FILE: translations/fr/user-handbook.md
================================================
# Manuel utilisateur de Babel
Ce document couvre tout ce que vous avez toujours voulu savoir sur l'utilisation de [Babel](https://babeljs.io) et des outils associés.
[](http://creativecommons.org/licenses/by/4.0/)
Ce manuel est disponible dans d'autres langues, voir le [README](/README.md) pour une liste complète.
# Sommaire
* [Introduction](#toc-introduction)
* [Mise en place de Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Exécution du CLI de Babel dans un projet](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuration de Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Exécution du code généré par Babel](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuration de Babel (Avancé)](#toc-configuring-babel-advanced)
* [Spécification manuelle des plugins](#toc-manually-specifying-plugins)
* [Options du plugin](#toc-plugin-options)
* [Personnalisation de Babel basé sur l'environnement](#toc-customizing-babel-based-on-environment)
* [Faire votre propre preset](#toc-making-your-own-preset)
* [Babel et les autres outils](#toc-babel-and-other-tools)
* [Outils d'analyse statique](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Style de code](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [IDEs et les éditeurs de texte](#toc-text-editors-and-ides)
* [Support Babel](#toc-babel-support)
* [Forum Babel](#toc-babel-forum)
* [Tchat Babel](#toc-babel-chat)
* [Issues (anomalies) de Babel](#toc-babel-issues)
* [Création d'un génial rapport de bogue](#toc-creating-an-awesome-babel-bug-report)
# Introduction
Babel est un compilateur générique et multi-usage pour JavaScript. L'utilisation de Babel, vous permet d'utiliser (et de créer) la prochaine génération de JavaScript, ainsi que la prochaine génération des outils JavaScript.
JavaScript est un langage en constante évolution, avec sans cesse de nouvelles spécifications, propositions et fonctionnalités. L'usage de Babel vous permettra d'utiliser un grand nombre de ces fonctionnalités des années avant qu'elles ne soient disponibles partout.
Babel fait cela en compilant le code JavaScript écrit avec le dernier standard dans une version fonctionnant partout aujourd'hui. Ce processus est connu comme de la compilation de source à source, aussi connu sous le nom de transpilation.
Par exemple, Babel pourrait transformer la syntaxe de la nouvelle fonction fléchée, de ceci :
```js
const square = n => n * n;
```
En cela :
```js
const square = function square(n) {
return n * n;
};
```
Cependant, Babel peut faire beaucoup plus, comme le support des extensions de syntaxe telles que JSX pour React et le support de Flow pour la vérification de type statique.
De plus, dans Babel tout est composé de module, et n'importe qui peut créer le sien en utilisant toute la puissance de Babel pour en faire ce qu'il souhaite.
*Bien plus encore*, Babel est décomposé en un certain nombre de modules de base que n'importe qui peut utiliser pour construire la prochaine génération d'outils pour JavaScript.
Beaucoup de gens l'ont déjà fait, et l'écosystème qui a surgi autour de Babel est massif et très diversifié. Tout au long de ce guide, je vais couvrir ces deux aspects, comment les outils intégrés de Babel fonctionnent ainsi que des choses utiles provenant de la communauté.
> ***Pour les prochaines mises à jour, suivez [@thejameskyle](https://twitter.com/thejameskyle) sur Twitter.***
* * *
# Mise en place de Babel
Puisque la communauté JavaScript dispose d'une multitude d'outils de construction, de frameworks, de plateformes, etc., Babel fournit des intégrations officielles pour la majorité des outils. Peu importe votre configuration, en allant de Gulp à Browserify, ou bien d'Ember à Meteor, il y a probablement une intégration officielle.
Dans le cadre de ce guide, nous allons seulement couvrir les méthodes par défaut pour configurer Babel, mais vous pouvez également visiter la [page de configuration](http://babeljs.io/docs/setup) interactive pour les autres intégrations.
> **Remarque :** Ce guide va se référer à des outils de ligne de commande comme `node` et `npm`. Avant d'aller plus loin, vous devez être à l'aise avec ces outils.
## `babel-cli`
Le CLI de Babel est une façon simple de compiler des fichiers avec Babel depuis la ligne de commande.
Nous allons d’abord l’installer globalement pour apprendre les bases.
```sh
$ npm install --global babel-cli
```
Nous pouvons compiler notre premier fichier comme suit :
```sh
$ babel my-file.js
```
Cela affichera le résultat compilé directement dans votre terminal. Pour l’écrire dans un fichier, nous devons le préciser avec `--out-file` ou `-o`.
```sh
$ babel example.js --out-file compiled.js
# ou
$ babel example.js -o compiled.js
```
Si nous voulons compiler un répertoire entier dans un nouveau répertoire, nous pouvons le faire en utilisant `--out-dir` ou `-d`.
```sh
$ babel src --out-dir lib
# ou
$ babel src -d lib
```
### Exécution du CLI de Babel dans un projet
Bien que vous *pouvez* installer le CLI de Babel globalement sur votre machine, il est préférable de l'installer **localement** pour chaque projet.
Il y a deux raisons principales à cela.
1. Plusieurs projets sur la même machine peuvent dépendre de différentes versions de Babel, donc ceci vous permet d'en mettre un à jour à la fois.
2. Cela signifie que vous n’avez pas une dépendance implicite sur l’environnement que vous utilisez. Ce qui rend votre projet beaucoup plus portable et plus facile à installer.
Nous pouvons installer localement le CLI de Babel en exécutant :
```sh
$ npm install --save-dev babel-cli
```
> **Remarque :** Comme c’est généralement une mauvaise idée d’exécuter Babel globalement, vous pouvez désinstaller la copie globale en exécutant :
>
> ```sh
$ npm uninstall --global babel-cli
```
Après avoir terminé l’installation, votre fichier `package.json` devrait ressembler à ceci :
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Maintenant au lieu d’exécuter Babel directement à partir de la ligne de commande, nous allons mettre nos commandes dans les **scripts npm** qui utiliseront notre version locale.
Ajoutez simplement un champ `"scripts"` à votre `package.json` et mettez la commande de babel dans `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Maintenant depuis notre terminal nous pouvons exécuter :
```js
npm run build
```
Ceci lancera Babel de la même manière que précédemment, seulement maintenant, nous utilisons une copie locale.
## `babel-register`
La méthode suivante, la plus courante pour l’exécution de Babel, se fait par le biais de `babel-register`. Cette option vous permettra d’exécuter Babel en exigeant seulement les fichiers qui peuvent le mieux s’intégrer à votre configuration.
Notez que ce n'est pas destiné à une utilisation en production. Cela est considéré comme une mauvaise pratique de déployer du code qui est compilé de cette façon. Il est préférable de le compiler à l’avance avant de déployer. Toutefois, cela fonctionne très bien pour les scripts de construction ou d'autres choses que vous exécutez localement.
Premièrement, nous allons créer un fichier `index.js` dans notre projet.
```js
console.log("Hello world!");
```
Si nous devions exécuter ceci avec `node index.js`, ce ne serait pas compilé avec Babel. Donc, au lieu de faire cela, nous allons configurer `babel-register`.
Tout d’abord installez `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Ensuite, créez un fichier `register.js` dans le projet et écrivez le code suivant :
```js
require("babel-register");
require("./index.js");
```
Ce qu’il fait, c'est d'*inscrire* Babel dans le système de module de Node et de commencer à compiler tous les fichiers qui sont exigés (require).
Maintenant, au lieu d’exécuter `node index.js`, nous pouvons utiliser à la place `register.js`.
```sh
$ node register.js
```
> **Remarque :** Vous ne pouvez pas enregistrer Babel dans le même fichier que celui que vous souhaitez compiler. Car node exécute le fichier avant que Babel ait une chance de le compiler.
>
> ```js
require("babel-register");
// pas compilé :
console.log("Hello world!");
```
## `babel-node`
Si vous êtes juste en train d’exécuter du code via le CLI de `node`, la meilleure façon d’intégrer Babel serait d’utiliser le CLI de `babel-node` qui est essentiellement un remplaçant du CLI de `node`.
Notez que ce n'est pas destiné à une utilisation en production. Cela est considéré comme une mauvaise pratique de déployer du code qui est compilé de cette façon. Il est préférable de le compiler à l’avance avant de déployer. Toutefois, cela fonctionne très bien pour les scripts de construction ou d'autres choses que vous exécutez localement.
Tout d’abord, assurez-vous que vous avez `babel-cli` qui est installé.
```sh
$ npm install --save-dev babel-cli
```
> **Remarque :** Si vous vous demandez pourquoi nous installons ceci en locale, veuillez lire ci-dessus la section [Exécution du CLI de Babel au sein d’un projet](#toc-running-babel-cli-from-within-a-project).
Puis remplacer chaque fois que vous exécutez `node` avec `babel-node`.
Si vous utilisez les `scripts` de npm, vous pouvez simplement faire :
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Dans le cas contraire, vous devrez écrire vous-même le chemin d’accès à `babel-node`.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Astuce : Vous pouvez également utiliser [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
Si vous devez utiliser Babel par programmation, pour une raison quelconque, vous pouvez utiliser le package `babel-core`.
Tout d’abord installez `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
Si vous avez une chaîne de JavaScript, vous pouvez la compiler directement à l’aide de `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
Si vous travaillez avec des fichiers, vous pouvez utiliser l’api asynchrone :
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Ou l’api synchrone :
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
Si pour une raison quelconque vous avez déjà un AST de Babel, vous pouvez le transformer directement depuis l’AST.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
Pour toutes les méthodes ci-dessus, les `options` se réfèrent à https://babeljs.io/docs/usage/api/#options.
* * *
# Configuration de Babel
Vous avez peut-être remarqué qu’en exécutant Babel sur lui-même, il ne semble pas faire autre chose que de la copie de fichier JavaScript d’un endroit à un autre.
C’est parce que nous n’avons pas dit à Babel de faire quoi que ce soit pour le moment.
> Comme Babel est un compilateur polyvalent, et qu'il est utilisé de plusieurs façons différentes, il ne fait rien par défaut. Vous devez indiquer explicitement ce que Babel doit faire.
Vous pouvez donner des instructions Babel sur ce qu’il doit faire en installant des **plugins** ou **presets** (groupes de plugins).
## `.babelrc`
Avant de commencer à dire à Babel ce qu'il doit faire. Nous devons créer un fichier de configuration. Tout ce que vous devez faire, c'est créer un fichier `.babelrc` à la racine de votre projet. Commencez le comme ceci :
```js
{
"presets": [],
"plugins": []
}
```
Ce fichier explique comment configurer Babel pour faire ce que vous voulez.
> **Remarque :** Bien que vous puissiez passer également des options à Babel de manières différentes, le fichier `.babelrc` est la convention et c'est la meilleure façon de le faire.
## `babel-preset-es2015`
Commençons par dire à Babel de compiler ES2015 (la version la plus récente de la norme JavaScript, également connu sous le nom de ES6) en ES5 (la version aujourd'hui disponible dans la plupart des environnements de JavaScript).
Nous ferons cela en installant le preset de Babel "es2015" :
```sh
$ npm install --save-dev babel-preset-es2015
```
Ensuite, nous allons modifier notre `.babelrc` pour inclure ce preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
La mise en place de React est tout aussi facile. Il suffit d’installer le preset :
```sh
$ npm install --save-dev babel-preset-react
```
Puis ajoutez le preset à votre fichier `.babelrc` :
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript a aussi quelques propositions qui font leur chemin dans la norme par le biais du processus de la TC39 (Comité technique derrière le standard ECMAScript).
Ce processus est découpé en 5 étapes (0-4). Plus les propositions séduisent et plus elles sont susceptibles d'être acceptées dans la norme, elles passent par les différentes étapes pour être finalement acceptées dans la norme à l'étape 4.
Celles-ci sont groupées dans babel comme 4 preset différents :
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Remarquez qu’il n’y a pas de preset stage-4, car il se trouve tout simplement dans le preset `es2015`.
Chacun de ces presets nécessite le preset des étapes ultérieures. C'est-à-dire que `babel-preset-stage-1` a besoin de `babel-preset-stage-2` qui lui même a besoin de `babel-preset-stage-3`.
Installez simplement l'étape que vous souhaitez utiliser :
```sh
$ npm install --save-dev babel-preset-stage-2
```
Puis vous pouvez l’ajouter au config de votre `.babelrc`.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Exécution du code généré par Babel
Donc vous avez compilé votre code avec Babel, mais ce n’est pas la fin de l’histoire.
## `babel-polyfill`
Presque toute la syntaxe JavaScript futuriste peut être compilée avec Babel, mais ce n'est pas le cas pour les API.
Par exemple, le code suivant est une fonction flèchée qui doit être compilé :
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Qui se transforme en ceci :
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
Malgré cela, le code ne fonctionnera pas encore partout parce que `Array.from` n’existe pas dans tous les environnements JavaScript.
Uncaught TypeError: Array.from is not a function
Pour résoudre ce problème, nous utilisons ce qu’on appelle un [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Autrement dit, un polyfill est un morceau de code qui reproduit une api native qui n’existe pas dans le runtime actuel. Cela vous permet d’utiliser des API telles que `Array.from` avant qu’elles ne soient disponibles.
Babel utilise l'excellent [core-js](https://github.com/zloirock/core-js) comme son polyfill, ainsi qu’un runtime personnalisé [regenerator](https://github.com/facebook/regenerator) pour obtenir des générateurs et des fonctions async qui fonctionnent.
Pour inclure le polyfill Babel, veuillez d’abord l’installez avec npm :
```sh
$ npm install --save babel-polyfill
```
Ensuite, veuillez simplement inclure le polyfill en haut de n’importe quel fichier qui l’exige :
```js
import "babel-polyfill";
```
## `babel-runtime`
Pour implémenter les spécifications ECMAScript, Babel utilisera des méthodes "d'aide" (helper) afin de garder le code généré propre.
Comme la liste de ces aides peut devenir assez longue et qu'elle est ajoutée au début de chaque fichier, vous pouvez utiliser un seul "runtime" qui utilisera seulement celles qui sont requises.
Commencez par installer `babel-plugin-transform-runtime` et `babel-runtime` :
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Puis mettez à jour votre `.babelrc` :
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Maintenant, Babel compilera le code qui suit :
```js
class Foo {
method() {}
}
```
En cela :
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Au lieu de mettre les aides `_classCallCheck` et `_createClass` dans chaque fichier, Babel les mettra où elles sont nécessaires.
* * *
# Configuration de Babel (Avancé)
La plupart des gens utilisent seulement Babel avec les presets fournis, cependant, Babel propose un contrôle beaucoup plus grand que cela.
## Spécification manuelle des plugins
Les presets de Babel sont simplement des collections de plugins pré-configurés, si vous voulez faire les choses différemment, vous devez spécifier manuellement les plugins. Cela fonctionne presque exactement de la même façon que les presets prédéfinis.
Commencez par installer un plugin :
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Ajoutez ensuite le champ `plugins` à votre `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
Cela vous donne un contrôle beaucoup plus fin sur les transformations que vous exécutez.
Pour avoir une liste complète des plugins officiels, voir la [page des plugins de Babel](http://babeljs.io/docs/plugins/).
Jetez aussi un œil à tous les plugins qui ont été [construits par la communauté](https://www.npmjs.com/search?q=babel-plugin). Si vous souhaitez apprendre comment écrire votre propre plugin, lisez le [Manuel du plugin Babel](plugin-handbook.md).
## Options du plugin
De nombreux plugins ont aussi des options, cela permet de les configurer afin qu'ils se comportent différemment. Par exemple, plusieurs transformations ont un mode "loose" qui enlève certains comportements de la spécification en faveur d'une solution plus simple et plus performante du code généré.
Pour ajouter des options à un plugin, apportez simplement la modification suivante :
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> Je vais travailler sur les mises à jour de la documentation du plugin pour détailler toutes les options dans les prochaines semaines. [Suivez-moi pour les mises à jour](https://twitter.com/thejameskyle).
## Personnalisation de Babel basé sur l'environnement
Les plugins de Babel solutionnent plusieurs tâches différentes. Nombre d'entre eux sont des outils de développement qui peuvent vous aider à déboguer votre code ou intégrer des outils. Il y a aussi beaucoup de plugins qui sont destinés à optimiser votre code en production.
Pour cette raison, il est fréquent de vouloir avoir une configuration de Babel basée sur l’environnement. Vous pouvez le faire facilement avec votre fichier `.babelrc`.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel activera la configuration à l’intérieur de `env` par rapport à l’environnement actuel.
L’environnement actuel utilise `process.env.BABEL_ENV`. Quand `BABEL_ENV` n’est pas disponible, il se replit vers `NODE_ENV`, et s'il n’est pas disponible, par défaut c'est `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Remarque :** `[COMMAND]` est celle que vous utilisez pour exécuter Babel (c’est à dire. `babel`, `babel-node` ou peut-être juste `node` si vous utilisez le hook du registre).
>
> **Astuce :** Si vous souhaitez que votre commande fonctionne à la fois sur des plates-formes unix et windows, utilisez [`cross-env`](https://www.npmjs.com/package/cross-env).
## Faire votre propre preset
Spécifiez manuellement les plugins ? Options du plugin ? Paramètres basés sur l’environnement ? Toute cette configuration peut vous sembler comme un nombre incalculable de répétition pour tous vos projets.
Pour cette raison, nous encourageons la communauté à créer leurs propres presets. Cela pourrait être un preset pour une [version spécifique de node](https://github.com/leebenson/babel-preset-node5) que vous utilisez, ou peut-être un preset pour [l’ensemble](https://github.com/cloudflare/babel-preset-cf) de votre [entreprise](https://github.com/airbnb/babel-preset-airbnb).
C'est facile de créer un preset. Disons que vous avez ce fichier `.babelrc` :
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
Tout ce que vous devez faire, c'est de créer un nouveau projet, en suivant la convention de nommage `babel-preset-*` (respecter cet espace de nommage) et créer deux fichiers.
Tout d’abord, créez un nouveau fichier `package.json` avec les `dépendances` nécessaires pour votre preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Puis créez un fichier `index.js` qui exporte le contenu de votre fichier `.babelrc`, en remplaçant les chaines plugin/preset avec des appels avec `require`.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Puis publiez simplement cela sur npm et vous pouvez l’utiliser comme vous le feriez pour un preset.
* * *
# Babel et les autres outils
Babel est assez simple à installer, une fois qu'on en a pris l'habitude, mais il peut être assez difficile à gérer lorsqu'on veut le mettre en place avec d’autres outils. Cependant, nous essayons de travailler en étroite collaboration avec d’autres projets afin de rendre l’expérience aussi facile que possible.
## Outils d'analyse statique
Les derniers standards apportent beaucoup de nouvelle syntaxe au langage et les outils d’analyse statique commencent tout juste à en profiter.
### Linting
Un des outils les plus populaires de linting est [ESLint](http://eslint.org), pour cette raison, nous maintenons une intégration officiel [`babel-eslint`](https://github.com/babel/babel-eslint).
Tout d’abord installer `eslint` et `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Ensuite, créez ou utilisez le fichier `.eslintrc` existant dans votre projet et définissez `parser` avec la valeur `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Maintenant, ajoutez une tâche `lint` à vos scripts npm dans le `package.json` :
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Ensuite, il suffit d'exécuter la tâche et vous aurez tous les réglages.
```sh
$ npm run lint
```
Pour plus d’informations, consultez la documentation de [`babel-eslint`](https://github.com/babel/babel-eslint) ou de [`eslint`](http://eslint.org).
### Style de code
> JSC a fusionné avec ESLint, donc vérifiez le style du code avec ESLint.
JSCS est un outil extrêmement populaire pour faire du linting un peu plus poussé en vérifiant le style du code. Un mainteneur principal à la fois du projet Babel et JSCS ([@hzoo](https://github.com/hzoo)) tient à jour une intégration officielle avec JSCS.
Mieux encore, cette intégration maintenant vit elle-même à l'intérieur de JSCS sous l'option `--esnext`. Donc l’intégration Babel est aussi simple que :
$ jscs . --esnext
Depuis la cli, ou en ajoutant l’option `esnext` à votre fichier `.jscsrc`.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
Pour plus d’informations, consultez la documentation de [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) ou de [`jscs`](http://jscs.info).
### Documentation
En utilisant Babel, ES2015 et Flow, vous pouvez prélever beaucoup de choses sur votre code. L'utilisation de [documentation.js](http://documentation.js.org) vous permet de générer une documentation détaillée de l'API très facilement.
Documentation.js utilise Babel en arrière plan pour prendre en charge l’ensemble de la syntaxe la plus récente, y compris les annotations de Flow afin de déclarer les types dans votre code.
## Frameworks
Tous les principaux frameworks JavaScript se concentrent maintenant sur l’alignement de leurs API sur le futur du langage. Pour cette raison, il y a beaucoup de travail en cours dans l’outillage.
Les frameworks ont la possibilité non seulement d’utiliser Babel, mais de l’étendre de façon à améliorer l’expérience des utilisateurs.
### React
React a radicalement changé son API pour s’aligner avec les classes de ES2015 ([Lisez la mise à jour de l’API ici](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Encore plus fort, React s'appuie sur Babel pour compiler sa syntaxe JSX, en abandonnant son propre outillage personnalisé en faveur de Babel. Vous pouvez commencer en mettant en place le package `babel-preset-react` en suivant les [instructions ci-dessus](#babel-preset-react).
La communauté de React a pris Babel et l'utilise. Il y a maintenant un certain nombre de transformations [construites par la communauté](https://www.npmjs.com/search?q=babel-plugin+react).
Plus particulièrement, le plugin [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform), qui combiné avec un certain nombre de [transformations spécifique de React](https://github.com/gaearon/babel-plugin-react-transform#transforms), peut permettre des choses comme *le rechargement à chaud de module* et autres utilitaires de débogage.
## IDEs et les éditeurs de texte
L'introduction de la syntaxe ES2015, JSX et Flow avec Babel peut s’avérer utile, mais si votre éditeur de texte ne le supporte pas, alors cela peut être vraiment une mauvaise expérience. Pour cette raison, vous voudrez configurer votre éditeur de texte ou l’IDE avec un plugin de Babel.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Support Babel
Babel a une communauté très importante et qui croît rapidement, comme nous grandissons, nous voulons nous assurer que les gens ont toutes les ressources dont ils ont besoin pour réussir. Nous offrons donc un certain nombre de canaux différents pour obtenir du soutien.
N’oubliez pas que, dans l’ensemble de ces communautés, nous appliquons un [Code de conduite](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). Si vous ne respectez pas le Code de conduite, des mesures seront prises. Alors veuillez le lire et en avoir conscience lorsque vous interagissez avec d’autres personnes.
Nous cherchons aussi à développer une communauté autonome, par des personnes qui restent dans les parages et qui aident les autres. Si vous trouvez quelqu'un qui pose une question, et que vous connaissez la réponse, prenez quelques minutes et aider-le. Faites de votre mieux pour être gentil et compréhensif lorsque vous le faites.
## Forum Babel
[Discourse](http://www.discourse.org) nous a fourni gratuitement une version hébergée de leur logiciel de forum (et nous les aimons pour cela !). Si les forums sont votre truc, veuillez vous arrêter sur [discuss.babeljs.io](https://discuss.babeljs.io).
## Tchat Babel
Tout le monde aime [Slack](https://slack.com). Si vous cherchez un soutien immédiat de la communauté, venez discuter avec nous sur [slack.babeljs.io](https://slack.babeljs.io).
## Issues (anomalies) de Babel
Babel utilise le suivi des problèmes fourni par [Github](http://github.com).
Vous pouvez voir tous les problèmes ouverts et fermés sur [Github](https://github.com/babel/babel/issues).
Si vous souhaitez ouvrir une nouvelle issue :
* [Recherchez dans les issues existantes](https://github.com/babel/babel/issues)
* [Créez un nouveau rapport de bogue](https://github.com/babel/babel/issues/new) ou [demandez une nouvelle fonctionnalité](https://github.com/babel/babel/issues/new)
### Création d'un génial rapport de bogue
Les issues de Babel peuvent parfois être très difficiles à déboguer à distance, donc nous avons besoin d'un maximum d’aide. Avec quelques minutes supplémentaires, l'élaboration d’un rapport de bogue vraiment sympa permet de résoudre le problème beaucoup plus rapidement.
Tout d’abord, essayez d’isoler votre problème. Il est extrêmement peu probable que chaque partie de votre configuration contribue au problème. Si votre problème est un morceau de code, essayez, en supprimant le plus de code possible, de provoquer toujours un problème.
> \[WIP\] (Travail en cours)
* * *
> ***Pour les prochaines mises à jour, suivez [@thejameskyle](https://twitter.com/thejameskyle) sur Twitter.***
================================================
FILE: translations/he/README.md
================================================
# Babel Handbook
This handbook is divided into two parts:
* [User Handbook](user-handbook.md) - How to setup/configure Babel and more.
* [Plugin Handbook](plugin-handbook.md) - How to create plugins for Babel.
> For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.
אם אתה קורא את המסמך הזה בגרסה מתורגמת, כלומר, שאיננה אנגלית, יכול להיות שתמצא חלקים שעוד לא תורגמו. אם תרצה לתרום לאחד התרגומים, עליך לעשות זאת דרך Crowdin. אנא תקרא את [כללי התרומה](/CONTRIBUTING.md) למידע נוסף. You will find a number of english words that are programming concepts. If these were translated to other languages there would be a lack of consistency and fluency when reading about them. In many cases you will find the literal translation followed by the english term in parenthesis `()`. For example: Abstract Syntax Trees (ASTs).
================================================
FILE: translations/he/plugin-handbook.md
================================================
# Babel Plugin Handbook
במסמך הזה נלמד כיצד ליצור [הרחבות](https://babeljs.io/docs/advanced/plugins/) ל-[Babel](https://babeljs.io).
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# תוכן העניינים
* [מבוא](#toc-introduction)
* [יסודות](#toc-basics)
* [ASTs](#toc-asts)
* [השלבים של Babel](#toc-stages-of-babel)
* [ניתוח (Parse)](#toc-parse)
* [ניתוח מילולי (Lexical Analysis)](#toc-lexical-analysis)
* [ניתוח תחבירי (Syntactic Analysis)](#toc-syntactic-analysis)
* [שינוי צורה (Transform)](#toc-transform)
* [יצירה](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Writing your first Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# מבוא
Babel is a generic multi-purpose compiler for JavaScript. More than that it is a collection of modules that can be used for many different forms of static analysis.
> Static analysis is the process of analyzing code without executing it. (Analysis of code while executing it is known as dynamic analysis). The purpose of static analysis varies greatly. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
You can use Babel to build many different types of tools that can help you be more productive and write better programs.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# יסודות
Babel is a JavaScript compiler, specifically a source-to-source compiler, often called a "transpiler". This means that you give Babel some JavaScript code, Babel modifies the code, and generates the new code back out.
## ASTs
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## השלבים של Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### ניתוח (Parse)
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### ניתוח מילולי (Lexical Analysis)
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### ניתוח תחבירי (Syntactic Analysis)
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### שינוי צורה (Transform)
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### יצירה
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Writing your first Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/he/user-handbook.md
================================================
# Babel User Handbook
This document covers everything you ever wanted to know about using [Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# תוכן העניינים
* [מבוא](#toc-introduction)
* [Setting up Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# מבוא
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can use (and create) the next generation of JavaScript, as well as the next generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Setting up Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
================================================
FILE: translations/hu/README.md
================================================
# Babel Handbook
This handbook is divided into two parts:
* [User Handbook](user-handbook.md) - How to setup/configure Babel and more.
* [Plugin Handbook](plugin-handbook.md) - How to create plugins for Babel.
> For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.
If you are reading a non-english translation of this handbook you may still find english sections that have not yet been translated. If you would like to contribute to one of the translations you must do so through Crowdin. Please read the [contributing guidelines](/CONTRIBUTING.md) for more information. You will find a number of english words that are programming concepts. If these were translated to other languages there would be a lack of consistency and fluency when reading about them. In many cases you will find the literal translation followed by the english term in parenthesis `()`. For example: Abstract Syntax Trees (ASTs).
================================================
FILE: translations/hu/plugin-handbook.md
================================================
# Babel Plugin Handbook
This document covers how to create [Babel](https://babeljs.io) [plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduction](#toc-introduction)
* [Basics](#toc-basics)
* [ASTs](#toc-asts)
* [Stages of Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Lexical Analysis](#toc-lexical-analysis)
* [Syntactic Analysis](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Writing your first Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introduction
Babel is a generic multi-purpose compiler for JavaScript. More than that it is a collection of modules that can be used for many different forms of static analysis.
> Static analysis is the process of analyzing code without executing it. (Analysis of code while executing it is known as dynamic analysis). The purpose of static analysis varies greatly. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
You can use Babel to build many different types of tools that can help you be more productive and write better programs.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Basics
Babel is a JavaScript compiler, specifically a source-to-source compiler, often called a "transpiler". This means that you give Babel some JavaScript code, Babel modifies the code, and generates the new code back out.
## ASTs
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Stages of Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Parse
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntactic Analysis
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transform
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Writing your first Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/hu/user-handbook.md
================================================
# Babel User Handbook
This document covers everything you ever wanted to know about using [Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduction](#toc-introduction)
* [Setting up Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Introduction
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can use (and create) the next generation of JavaScript, as well as the next generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Setting up Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
================================================
FILE: translations/id-ID/README.md
================================================
# Paduan Babel
Buku ini dibagi menjadi dua bagian:
* [Pengguna Handbook](user-handbook.md) - bagaimana setup/konfigurasi Babel dan banyak lagi.
* [Plugin Handbook](plugin-handbook.md) - bagaimana membuat plugin untuk Babel.
> Untuk pembaruan terbaru, ikuti [@thejameskyle](https://twitter.com/thejameskyle) di Twitter.
Jika anda membaca terjemahan non-inggris pada buku petunjuk ini, anda dapat menuju seksi bahasa inggris yang belum diterjemahkan. Jika anda ingin berkontribusi pada salah satu terjemahan, anda harus melakukannya melalui Crowdin. Mohon membaca [petunjuk kontribusi](/CONTRIBUTING.md) untuk informasi lebih lanjut. Anda akan menemukan sejumlah kata-kata bahasa Inggris yang adalah konsep-konsep pemrograman. Jika ini telah diterjemahkan ke bahasa lain akan ada kurangnya konsistensi dan kelancaran ketika membaca buku ini. Dalam banyak kasus, Anda akan menemukan terjemahan literal diikuti dengan istilah bahasa Inggris dalam kurung `()`. Sebagai contoh: Abstract Syntax Trees (ASTs).
================================================
FILE: translations/id-ID/plugin-handbook.md
================================================
# Pedoman Plungin Babel
Dokumen ini berisi langkah pembuatan [Babel](https://babeljs.io) [plugin](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
Buku pedoman ini tersedia dalam bahasa lain, lihat file [README](/README.md) untuk daftar lengkap.
# Daftar Isi
* [Pengenalan](#toc-introduction)
* [Dasar](#toc-basics)
* [ASTs](#toc-asts)
* [Status Babel](#toc-stages-of-babel)
* [Penguraian](#toc-parse)
* [Analisis Leksikal](#toc-lexical-analysis)
* [Analisis Sintaktik](#toc-syntactic-analysis)
* [Transformasi](#toc-transform)
* [Pembuatan](#toc-generate)
* [Traversal](#toc-traversal)
* [Pengunjung](#toc-visitors)
* [Jalur](#toc-paths)
* [Jalan pengunjung](#toc-paths-in-visitors)
* [Keadaan](#toc-state)
* [Cakupan](#toc-scopes)
* [Binding](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definisi](#toc-definitions)
* [Pembangun](#toc-builders)
* [Validator](#toc-validators)
* [Konverter](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Menulis Plugin Babel pertama Anda](#toc-writing-your-first-babel-plugin)
* [Operasi Transformasi](#toc-transformation-operations)
* [Pengunjungan](#toc-visiting)
* [Mendapatkan path dari Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Memeriksa tipe dari sebuah node](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulasi](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Ruang lingkup](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin: Opsi](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Bangunan node](#toc-building-nodes)
* [Praktik terbaik](#toc-best-practices)
* [Menghindari melintasi AST sebanyak mungkin](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Menggabungkan pengunjung sedapat mungkin](#toc-merge-visitors-whenever-possible)
* [Tidak melintasi ketika pencarian manual akan melakukan](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Mengoptimalkan pengunjung bertingkat](#toc-optimizing-nested-visitors)
* [Berhati-hati terhadap struktur bertingkat](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Pengenalan
Babel adalah compiler JavaScript umum yang multifungsi. Selebihnya, Babel adalah kumpulan modul yang dapat digunakan untuk beberapa bentuk analisis statik.
> Analisis statik adalah proses menganalisa kode tanpa mengeksekusinya. (Analisis kode selama mengeksekusi biasanya disebut analisis dinamik). Tujuan dari analisis statik itu beragam. Itu dapat digunakan untuk linting, compiling, higlight kode, transformasi kode, optimisasi, minifikasi, dan masih banyak lagi.
Anda dapat menggunakan Babel untuk membangun beberapa tipe perkakas yang dapat membantu anda semakin produktif dan menulis program lebih baik.
> ***Untuk pembaruan terbaru, ikuti [@thejameskyle](https://twitter.com/thejameskyle) di Twitter.***
* * *
# Dasar
Babel adalah compiler JavaScript, yang lebih spesifik sebuah kompiler sumber ke sumber, sering disebut "transpiler". Ini berarti bahwa anda memberi beberapa kode JavaScript kepada Babel, Babel memodifikasi kodenya, dan membuat kode baru dari proses di belakangnya.
## ASTs
Setiap langkah meliputi pembuatan atau pengerjaan [Pohon Sintak Abstrak](https://en.wikipedia.org/wiki/Abstract_syntax_tree) atau AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Periksa [AST Explorer](http://astexplorer.net/) untuk mendapatkan informasi yang lebih baik untuk node AST. [Ini](http://astexplorer.net/#/Z1exs6BWMq) adalah linknya dengan kode contoh di atas disisipkan.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Atau sebagai objek JavaScript seperti ini:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Anda akan melihat bahwa setiap tingkat AST memiliki struktur serupa:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Catatan: Beberapa properti telah dihapus untuk disederhanakan.
Masing-masing dikenal sebagai **Node**. AST dapat terdiri dari satu node, atau ratusan jika tidak ribuan node. Bersama-sama mereka mampu menerangkan sintaks dari sebuah program yang dapat digunakan untuk analisis statis.
Setiap Node memiliki antarmuka berikut:
```typescript
interface Node {
type: string;
}
```
Field `type` adalah sebuah string yang mewakili tipe objek Node (seperti. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Setiap jenis Node mendefinisikan tambahan set properti yang menggambarkan tipe node tertentu.
Ada properti tambahan pada setiap Node bahwa Babel menghasilkan yang menggambarkan posisi Node dalam kode sumber aslinya.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Properti seperti `start`, `end`, `loc`, muncul dalam setiap Node tunggal.
## Status Babel
Tiga tahap utama dari Babel adalah **parse**, **transform**, **generate**.
### Parse
Tahap **parse**, mengambil kode dan mengembalikan AST. Ada dua fase mengurai di Babel: [**Analisis leksikal**](https://en.wikipedia.org/wiki/Lexical_analysis) dan [**Analisis sintaksis**](https://en.wikipedia.org/wiki/Parsing).
#### Analisis Leksikal
Analisis leksikal akan mengambil serangkaian kode dan mengubahnya menjadi aliran **tokens**.
Anda dapat memikirkan tokens sebagai larik datar potongan sintaks bahasa.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Masing-masing `type` di sini memiliki seperangkat sifat-sifat yang menggambarkan token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Seperti AST node mereka juga memiliki `start`, `end`, dan `loc`.
#### Analisis Sintaktik
Analisis sintaksis akan mengambil aliran token dan mengubahnya menjadi representasi AST. Menggunakan informasi dalam token, fase ini akan memformat mereka sebagai AST yang mewakili struktur kode dalam cara yang membuatnya lebih mudah untuk bekerja.
### Transformasi
Tahap [transformasi](https://en.wikipedia.org/wiki/Program_transformation) mengambil AST dan melintasi, menambahkan, memperbarui, dan menghapus node dalam prosesnya. Ini adalah bagian paling kompleks dari Babel atau kompiler apapun. Ini adalah tempat plugin beroperasi dan ini akan berupa sebagian besar buku ini. Jadi kita tidak akan menyelam terlalu jauh sekarang.
### Pembuatan
Tahap [kode generasi](https://en.wikipedia.org/wiki/Code_generation_(compiler)) mengambil AST akhir dan mengubahnya kembali ke serangkaian kode, juga menciptakan [sumber peta](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Pembuatan kode ini cukup sederhana: Anda melintasi melalui AST pada kedalaman pertama, membangun sebuah string yang mewakili kode yang sudah ditransormasi.
## Traversal
Bila Anda ingin mengubah AST Anda harus [melintasi pohon](https://en.wikipedia.org/wiki/Tree_traversal) secara rekursif.
Mengatakan bahwa kita memiliki tipe `FunctionDeclaration`. Memiliki beberapa sifat: `id`, `params` dan `body`. Masing-masing memiliki node bertingkat.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Jadi kita mulai pada `FunctionDeclaration` dan kita tahu sifat internal sehingga kami mengunjungi masing-masing fungsi dan anak-anak mereka dalam urutan.
Selanjutnya kita pergi ke `id` yang adalah sebuah `Identifier`. `Identifier` tidak memiliki sifat setiap node anak sehingga kita dapat melanjutkan.
Setelah itu adalah `params` yang merupakan array node jadi kita mengunjungi masing-masing. Dalam hal ini sebuah node yang juga sebuah `Identifier` sehingga kita dapat lanjutkan.
Kemudian kita menggunakan `body` yang merupakan `BlockStatement` dengan properti `body` yang merupakan node array agar kita bisa menggunakannya masing-masing.
Satu-satunyaitem di sini adalah sebuah node `ReturnStatement` yang memiliki `argumen`, kita bisa menggunakan `argumen` dan menemukan `BinaryExpression`.
`BinaryExpression` memiliki `operator`, `left`, dan `right`. Operator bujanlah sebuah node, hanya sebuah nilai, jadi kita tidak menggunakannya, dan hanya menggunakan `left` dan `right`.
Proses traversal ini terjadi sepanjang tahap mengubah Babel.
### Pengunjung
Ketika kita berbicara tentang "pergi" ke sebuah node, kita benar-benar mengartikan untuk **mengunjungi** mereka. Kita menggunakan istilah itu alasannya karena ada konsep [**pengunjung**](https://en.wikipedia.org/wiki/Visitor_pattern).
Pengunjung adalah sebuah pola yang digunakan dalam AST traversal bahasa. Cukup menempatkan sebuah objek dengan metode yang ditentukan untuk menerima tipe node tertentu di pohon. Itu sedikit abstrak jadi mari kita lihat sebuah contoh.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Catatan:** `Identifier() { ... }` adalah singkatan untuk `Identifier: {enter() { ... }}`.
Ini adalah dasar pengunjung ketika digunakan selama traversal akan memanggil metode `Identifier()` untuk setiap `Identifier` di pohon.
Jadi dengan kode `Identifier()` metode akan dipanggil empat kali dengan masing-masing `Identifier` (termasuk `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
Panggilan ini adalah semua node **Masukkan**. Namun ada juga kemungkinan memanggil metode pengunjung ketika pada **keluar**.
Bayangkan kita memiliki struktur ini:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
Seperti kita melintasi turun setiap cabang pohon kita akhirnya memukul mati berakhir di mana kita perlu untuk melintasi cadangan pohon untuk mendapatkan ke node berikutnya. Turun pohon kita **Masukkan** setiap node, maka akan kembali kita **keluar** setiap node.
Mari kita *berjalan* melalui apa proses ini tampak seperti untuk pohon di atas.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
Jadi ketika membuat pengunjung Anda memiliki dua kesempatan untuk mengunjungi sebuah node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Jalur
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Jalan pengunjung
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Keadaan
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Cakupan
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Binding
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Catatan: Ini bukanlah pengganti untuk rinci dokumentasi API yang akan tersedia di tempat lain segera.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Catatan:** `sourceType` default `"script"` dan akan kesalahan ketika ia menemukan `impor` atau `ekspor`. Lulus `sourceType: "modul"` untuk menyingkirkan kesalahan ini.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definisi
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Pembangun
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validator
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Konverter
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Menulis Plugin Babel pertama Anda
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Operasi Transformasi
## Pengunjungan
### Mendapatkan path dari Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Memeriksa tipe dari sebuah node
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulasi
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Ruang lingkup
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin: Opsi
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Bangunan node
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Praktik terbaik
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Menghindari melintasi AST sebanyak mungkin
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Menggabungkan pengunjung sedapat mungkin
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Tidak melintasi ketika pencarian manual akan melakukan
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Mengoptimalkan pengunjung bertingkat
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Berhati-hati terhadap struktur bertingkat
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/id-ID/user-handbook.md
================================================
# Paduan Pengguna Babel
Dokumen ini mencakup semua yang ingin Anda tahu tentang menggunakan [Babel](https://babeljs.io) dan terkait tooling.
[](http://creativecommons.org/licenses/by/4.0/)
Buku pedoman ini tersedia dalam bahasa lain, lihat [README](/README.md) untuk daftar lengkap.
# Daftar Isi
* [Pengenalan](#toc-introduction)
* [Mempersiapkan Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Menjalankan Babel CLI dalam sebuah proyek](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Mengkonfigurasi Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Mengeksekusi kode yang dihasilkan Babel](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Mengkonfigurasi Babel (lanjutan)](#toc-configuring-babel-advanced)
* [Menentukan plugin secara manual](#toc-manually-specifying-plugins)
* [Pilihan plugin](#toc-plugin-options)
* [Menyesuaikan Babel berdasarkan lingkungan](#toc-customizing-babel-based-on-environment)
* [Membuat preset Anda sendiri](#toc-making-your-own-preset)
* [Babel dan alat lainnya](#toc-babel-and-other-tools)
* [Alat analisis statis](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Gaya kode](#toc-code-style)
* [Dokumentasi](#toc-documentation)
* [Kerangka kerja](#toc-frameworks)
* [React](#toc-react)
* [Teks editor dan IDE](#toc-text-editors-and-ides)
* [Dukungan Babel](#toc-babel-support)
* [Forum Babel](#toc-babel-forum)
* [Chat Babel](#toc-babel-chat)
* [Masalah Babel](#toc-babel-issues)
* [Membuat laporan bug Babel yang mengagumkan](#toc-creating-an-awesome-babel-bug-report)
# Pengenalan
Babel adalah kompiler generik multi-fungsi untuk JavaScript. Dengan menggunakan Babel Anda dapat menggunakan (dan membuat) JavaScript generasi berikutnya, serta tooling JavaScript generasi berikutnya.
JavaScript sebagai bahasa yang terus berkembang, dengan spesifikasi terbaru dan proposal yang keluar dengan fitur terbaru sepanjang waktu. Menggunakan Babel akan memungkinkan Anda untuk menggunakan berbagai fitur terbaru sebelum mereka tersedia.
Babel melakukan ini dengan menyusun turun kode JavaScript yang ditulis dengan standar terbaru ke versi yang akan bekerja di mana-mana hari ini. Proses ini dikenal sebagai source-to-source compiling, juga dikenal sebagai transpiling.
Sebagai contoh, Babel bisa mengubah sintaks arrow-function pada ES2015 dari:
```js
const square = n => n * n;
```
Menjadi:
```js
const square = function square(n) {
return n * n;
};
```
Selain itu, Babel dapat melakukan lebih banyak hal lagi karena Babel dapat mengekstensi sintaks lain seperti sintaks JSX untuk React dan sintaks Flow untuk static type checking.
Lebih dari itu, semuanya di Babel hanya sebuah plugin dan siapa saja bisa menggunakan dan membuat plugin mereka sendiri yang menggunakan "kekuatan penuh" dari Babel untuk melakukan apa pun yang mereka inginkan.
*Bahkan lebih* dari itu, Babel dibagi menjadi beberapa modul inti yang siapa pun dapat menggunakannya untuk membangun tooling Javascript generasi berikutnya.
Banyak orang juga telah melakukannya, ekosistem yang bermunculan di sekitar Babel sangat besar dan beragam. Melalui buku ini saya akan membahas baik bagaimana tool Babel bekerja dan juga beberapa hal yang berguna yang terkumpul dari seluruh komunitas.
> ***Untuk pembaruan terbaru, ikuti [@thejameskyle](https://twitter.com/thejameskyle) di Twitter.***
* * *
# Mempersiapkan Babel
Karena komunitas JavaScript banyak memiliki build tool, kerangka, platform, dll, Babel juga memiliki semua integrasi resmi untuk semua build tool. Semuanya dari Gulp sampai Browserify, dari Ember sampai dengan Meteor, tidak peduli apapun pengaturan yang anda buat, mungkin saja ada integrasi resminya.
Sesuai tujuan buku ini, kita hanya akan membahas cara-cara built-in menyiapkan Babel, tetapi Anda juga dapat mengunjungi [halaman setup](http://babeljs.io/docs/setup) interaktif untuk semua integrasi.
> **Catatan:** Panduan ini akan merujuk pada baris perintah perangkat seperti `node` dan `npm`. Sebelum melanjutkan lebih jauh Anda harus nyaman dengan tool ini.
## `babel-cli`
Babel's CLI adalah cara sederhana untuk meng-compile file dengan Babel dari command line atau terminal.
Pertama mari kita menginstalnya secara global untuk mempelajari dasar-dasar.
```sh
$ npm install --global babel-cli
```
Kita dapat meng-compile file pertama kita seperti:
```sh
$ babel my-file.js
```
Ini akan menghasilkan output yang ter-compile langsung ke terminal Anda. Untuk menulisnya ke dalam file kita dapat menambahkan `--out-file` atau `-o`.
```sh
$ babel example.js --out-file compiled.js
# atau
$ babel example.js -o compiled.js
```
Jika kita ingin meng-compile seluruh direktori ke direktori baru kita dapat melakukannya dengan menggunakan `-out-dir` atau `-d`.
```sh
$ babel src --out-dir lib
# atau
$ babel src -d lib
```
### Menjalankan Babel CLI dalam sebuah project
Meskipun Anda *dapat* menginstal Babel CLI secara global, Anda lebih baik untuk menginstalnya **secara lokal** untuk setiap project.
Ada dua alasan utama untuk ini.
1. Project-project yang berbeda di mesin yang sama bisa bergantung pada versi Babel yang berbeda, yang memungkinkan Anda untuk memperbarui salah satunya.
2. Itu berarti Anda tidak memiliki ketergantungan implisit terhadap environment mesin anda. Membuat project Anda jauh lebih portabel dan mudah untuk di-setup.
Kita dapat menginstal Babel CLI secara lokal dengan menjalankan:
```sh
$ npm install --save-dev babel-cli
```
> **Catatan:** Karena menjalankan Babel secara global adalah ide yang buruk, Anda mungkin ingin menghapus instalasi Babel global dengan menjalankan:
>
> ```sh
$ npm uninstall --global babel-cli
```
Setelah selesai menginstal, file `package.json` Anda harusnya terlihat seperti ini:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Sekarang, daripada menjalankan Babel langsung dari command line, kita akan memasukkan command kita di dalam **npm script** yang tentunya akan menggunakan Babel yang terinstal di dalam folder project.
Cukup tambahkan sebuah field `"scripts"` di `package.json` dan tulis command babel di dalam sana sebagai `"build"`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Sekarang dari terminal kita dapat menjalankan:
```js
npm run build
```
Perintah ini akan menjalankan Babel dengan cara yang sama seperti sebelumnya, namun perbedaanya sekarang kita menggunakan Babel yang terinstal di dalam folder project.
## `babel-register`
Metode yang paling umum berikutnya untuk menjalankan Babel adalah melalui `babel-register`. Cara ini akan memungkinkan Anda untuk menjalankan Babel hanya dengan meng-"require()" sebuah file.
Catatan: Cara ini tidak dimaksudkan untuk production. Hal ini dianggap bad-practice untuk menjalankan babel-register di dalam server production. Lebih baik untuk meng-compile project anda terlebih dahulu sebelum di-deploy ke production. Walaupun tidak baik digunakan di production, cara ini terbilang cukup baik untuk digunakan ketika sedang men-develop project di mesin lokal kita.
Pertama mari kita buat `index.js` file di dalam folder project kita.
```js
console.log("Hello world!");
```
Jika kita jalankan dengan `node index.js`, kode kita tidak akan di-compile dengan Babel. Jadi daripada melakukannya dengan cara tersebut, kita akan setup `babel-register`.
Pertama install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Selanjutnya, buat file `register.js` dalam folder project dan tuliskan kode berikut ini:
```js
require("babel-register");
require("./index.js");
```
Maksud kode tersebut adalah *menambahkan* Babel di modul sistem Node dan mulai meng-compile setiap file yang di- `require()`.
Sekarang, daripada menjalankan `node index.js` kita dapat menggunakan `register.js` sebagai gantinya.
```sh
$ node register.js
```
> **Catatan:** Anda tidak dapat mendaftarkan Babel pada file yang akan anda compile. Karena Node akan mengeksekusi file sebelum Babel memiliki kesempatan untuk meng-compile-nya.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
Jika Anda hanya menjalankan beberapa kode melalui `node` CLI cara termudah untuk mengintegrasikan Babel mungkin menggunakan `babel-node` CLI yang sebagian besar hanya penurunan pengganti `node` CLI.
Catatan: Cara ini tidak dimaksudkan untuk production. Hal ini dianggap bad-practice untuk menjalankan babel-register di dalam server production. Lebih baik untuk meng-compile project anda terlebih dahulu sebelum di-deploy ke production. Walaupun tidak baik digunakan di production, cara ini terbilang cukup baik untuk digunakan ketika sedang men-develop project di mesin lokal kita.
Pertama, pastikan bahwa Anda memiliki `babel-cli` diinstal.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Lalu timpa dimanapun Anda menjalankan `node` dengan `babel node`.
Jika Anda menggunakan npm `scripts` Anda dapat melakukan:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Sebaliknya Anda akan perlu untuk menulis path ke `babel-node` itu sendiri.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: Anda juga dapat menggunakan [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
Jika Anda perlu menggunakan Babel pemrograman untuk beberapa alasan, Anda dapat menggunakan paket `babel-core` itu sendiri.
Pertama Instal `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
Jika Anda memiliki serangkaian JavaScript Anda dapat mengkompilasi langsung menggunakan `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
Jika Anda bekerja dengan file, Anda dapat menggunakan baik api tak sinkron:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Atau api sinkron:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
Jika Anda sudah memiliki AST Babel untuk alasan apa pun Anda mungkin mengubah dari AST langsung.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Mengkonfigurasi Babel
Anda mungkin telah memperhatikan oleh sekarang bahwa menjalankan Babel sendiri tampaknya tidak melakukan apa-apa selain menyalin file JavaScript dari satu lokasi lain.
Hal ini karena kita belum diberitahu Babel untuk melakukan apa-apa lagi.
> Karena Babel kompiler tujuan umum yang akan digunakan dalam berbagai cara yang berbeda, tidak melakukan apa pun secara default. Anda harus secara eksplisit memberitahu Babel apa yang harus ia lakukan.
Anda dapat memberikan petunjuk Babel tentang apa yang harus dilakukan dengan menginstal **plugin** atau **presets** (kelompok plugin).
## `.babelrc`
Sebelum kita mulai menceritakan Babel apa yang harus dilakukan. Kita perlu membuat file konfigurasi. Yang perlu Anda lakukan adalah membuat `.babelrc` file akar dari proyek Anda. Memulai dengan itu seperti ini:
```js
{
"presets": [],
"plugins": []
}
```
File ini adalah bagaimana Anda mengkonfigurasi Babel untuk melakukan apa yang Anda inginkan.
> **Catatan:** Sementara Anda juga dapat melewati pilihan untuk Babel dengan cara lain `.babelrc` file Konvensi dan cara terbaik.
## `babel-preset-es2015`
Mari kita mulai dengan mengatakan Babel untuk mengkompilasi ES2015 (versi terbaru JavaScript standar, juga dikenal sebagai ES6) untuk ES5 (versi tersedia dalam kebanyakan lingkungan JavaScript hari).
Kami akan melakukan hal ini dengan menginstal "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Selanjutnya kita akan memodifikasi kami `.babelrc` untuk menyertakan pra-atur itu.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Menyiapkan React sama mudahnya. Hanya menginstal preset:
```sh
$ npm install --save-dev babel-preset-react
```
Kemudian tambahkan preset untuk `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript juga memiliki beberapa proposal yang membuat jalan mereka ke dalam standar melalui TC39 (Komite teknis di balik standar ECMAScript) proses.
Proses ini rusak melalui proses tahap 5 (0-4). Proposal mendapatkan traksi yang lebih dan lebih mungkin untuk diterima ke dalam standar mereka melanjutkan melalui berbagai tahap, akhirnya diterima menjadi standar pada tahap 4.
Ini dibundel di babel sebagai 4 preset yang berbeda:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Perhatikan bahwa ada tidak ada tahap-4 preset seperti itu hanya `es2015` preset di atas.
Masing-masing preset ini memerlukan preset untuk tahap. yaitu `babel-preset-stage-1` mengharuskan `babel-preset-stage-2` yang memerlukan `babel-preset-stage-3`.
Hanya menginstal tahap Anda tertarik untuk menggunakan:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Kemudian Anda dapat menambahkannya ke konfigurasi `.babelrc`.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Mengeksekusi kode yang dihasilkan Babel
Jadi Anda telah menyusun kode Anda dengan Babel, tetapi ini bukanlah akhir dari cerita.
## `babel-polyfill`
Sintaks JavaScript futuristik hampir semua dapat dikompail dengan Babel, tetapi hal yang sama tidak berlaku untuk api.
Sebagai contoh, kode berikut memiliki fungsi panah yang perlu dikompilasi:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Yang berubah menjadi ini:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
Namun, ini masih tidak akan bekerja di mana-mana karena `Array.from` tidak ada di setiap lingkungan JavaScript.
Uncaught TypeError: Array.from is not a function
Untuk mengatasi masalah ini, kami menggunakan sesuatu yang disebut [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Sederhananya, polyfill adalah bagian dari kode yang mereplikasi api asli yang tidak ada di runtime saat ini. Memungkinkan Anda untuk menggunakan api seperti `Array.from` sebelum mereka tersedia.
Babel menggunakan baik [core-js](https://github.com/zloirock/core-js) sebagai polyfill yang, bersama dengan disesuaikan [regenerator](https://github.com/facebook/regenerator) runtime untuk mendapatkan generator dan fungsi async bekerja.
Untuk menyertakan Babel polyfill, pertama menginstalnya dengan npm:
```sh
$ npm install --save babel-polyfill
```
Kemudian hanya menyertakan polyfill di bagian atas setiap file yang membutuhkan itu:
```js
import "babel-polyfill";
```
## `babel-runtime`
Untuk menerapkan rincian ECMAScript spesifikasi, Babel akan menggunakan metode "Pembantu" untuk menjaga kode bersih.
Karena ini dapat mendapatkan cukup panjang, dan mereka ditambahkan ke atas setiap file, Anda dapat memindahkan mereka ke dalam satu "runtime" yang akan diperlukan.
Mulai dengan menginstal `babel-plugin-transform-runtime` dan `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Kemudian update Anda `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Sekarang Babel akan mengkompilasi kode seperti berikut:
```js
class Foo {
method() {}
}
```
Ke ini:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Daripada menempatkan penolong `_classCallCheck` dan `_createClass` dalam setiap satu file di mana mereka dibutuhkan.
* * *
# Mengkonfigurasi Babel (lanjutan)
Kebanyakan orang bisa mendapatkan dengan menggunakan Babel dengan hanya preset bawaan, tetapi Babel memperlihatkan banyak kekuasaan berbulir lebih halus dari itu.
## Menentukan plugin secara manual
Babel preset cukup koleksi pra-konfigurasi plugin, jika Anda ingin melakukan sesuatu yang berbeda Anda secara manual menentukan plugin. Ini bekerja hampir sama persis dengan cara yang sama sebagai preset.
Pertama Instal plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Kemudian tambahkan bidang `plugin` untuk Anda `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
Ini memberi Anda jauh lebih halus kendali atas mentransformasi tepat yang Anda menjalankan.
Untuk daftar lengkap plugin resmi Lihat [halaman plugin Babel](http://babeljs.io/docs/plugins/).
Juga lihat di semua plugin yang telah [dibangun oleh masyarakat](https://www.npmjs.com/search?q=babel-plugin). Jika Anda ingin belajar bagaimana menulis plugin Anda sendiri membaca [Babel Plugin Handbook](plugin-handbook.md).
## Pilihan plugin
Banyak plugin juga memiliki pilihan untuk mengkonfigurasi mereka untuk berperilaku berbeda. Sebagai contoh, banyak mengubah memiliki modus "longgar" yang tetes beberapa perilaku spec dalam mendukung lebih sederhana dan lebih performant dihasilkan kode.
Untuk menambahkan pilihan ke sebuah plugin, cukup membuat perubahan berikut:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> Aku akan bekerja pada update ke dokumentasi plugin untuk detail setiap pilihan dalam beberapa minggu mendatang. [Ikuti saya untuk update](https://twitter.com/thejameskyle).
## Menyesuaikan Babel berdasarkan lingkungan
Babel plugin memecahkan berbagai tugas. Banyak dari mereka adalah alat-alat pengembangan yang dapat membantu Anda debug kode Anda atau mengintegrasikan dengan alat. Ada juga banyak plugin yang dimaksudkan untuk mengoptimalkan kode Anda dalam produksi.
Untuk alasan ini, itu umum untuk menginginkan konfigurasi Babel yang didasarkan pada lingkungan. Anda dapat melakukannya dengan mudah dengan file `.babelrc` Anda.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel akan memungkinkan konfigurasi dalam `env` berdasarkan lingkungan saat ini.
Lingkungan saat ini akan menggunakan `process.env.BABEL_ENV`. Ketika `BABEL_ENV` tidak tersedia, itu akan mundur ke `NODE_ENV`, dan jika itu tidak tersedia, itu akan default untuk `"pembangunan"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Catatan:** `[COMMAND]` adalah apa pun yang Anda gunakan untuk menjalankan Babel (ie. `Babel`, `babel-node`, atau mungkin hanya `node` jika Anda menggunakan hook daftar).
>
> **Tip:** Jika Anda ingin perintah Anda untuk bekerja di platform unix dan windows kemudian menggunakan [`salib-env`](https://www.npmjs.com/package/cross-env).
## Membuat preset Anda sendiri
Secara manual menentukan plugin? Pilihan plugin? Pengaturan berbasis lingkungan? Semua konfigurasi ini mungkin tampak seperti satu ton pengulangan untuk semua proyek Anda.
Untuk alasan ini, kita mendorong komunitas untuk menciptakan preset mereka sendiri. Ini bisa preset untuk tertentu [node Versi](https://github.com/leebenson/babel-preset-node5) Anda menjalankan, atau mungkin preset untuk [seluruh](https://github.com/cloudflare/babel-preset-cf) [perusahaan](https://github.com/airbnb/babel-preset-airbnb).
Sangat mudah untuk menciptakan preset. Katakanlah Anda memiliki file `.babelrc` ini:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
Semua yang perlu Anda lakukan adalah membuat sebuah proyek baru yang mengikuti konvensi penamaan `babel - preset-*` (silahkan menjadi bertanggung jawab dengan namespace ini), dan membuat dua file.
Pertama, membuat file `package.json` baru dengan diperlukan `dependensi` Atur.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Kemudian membuat file `index.js` yang ekspor isi dari file `.babelrc` Anda, menggantikan plugin pra-atur string dengan `memerlukan` panggilan.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Kemudian hanya menerbitkan ini untuk npm dan Anda dapat menggunakannya seperti Anda akan preset apapun.
* * *
# Babel dan alat lainnya
Babel adalah cukup lurus ke depan untuk setup sekali Anda mendapatkan menguasainya, tetapi bisa agak sulit menavigasi cara mengatur dengan alat-alat lain. Namun, kami mencoba untuk bekerja sama dengan proyek-proyek lain untuk membuat pengalaman semudah mungkin.
## Alat analisis statis
Standar baru membawa banyak sintaks baru ke bahasa dan alat analisis statis hanya mulai mengambil keuntungan dari itu.
### Linting
Salah satu alat yang paling populer untuk linting [ESLint](http://eslint.org), karena ini kami menjaga integrasi resmi [`babel-eslint`](https://github.com/babel/babel-eslint).
Pertama Instal `eslint` dan `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Selanjutnya menciptakan atau menggunakan file `.eslintrc` yang sudah ada dalam proyek Anda dan mengatur `parser` sebagai `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Sekarang tambahkan tugas `serat` skrip `package.json` npm:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Kemudian hanya menjalankan tugas dan Anda akan semua setup.
```sh
$ npm run lint
```
Untuk informasi lebih lanjut lihat dokumentasi [`babel-eslint`](https://github.com/babel/babel-eslint) atau [`eslint`](http://eslint.org).
### Gaya kode
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS adalah alat yang sangat populer untuk mengambil linting langkah lebih jauh ke dalam memeriksa gaya kode itu sendiri. Pengelola inti Babel dan JSCS proyek ([@hzoo](https://github.com/hzoo)) mempertahankan integrasi resmi dengan JSCS.
Bahkan lebih baik, integrasi ini sekarang hidup dalam JSCS sendiri di bawah `--esnext` pilihan. Jadi mengintegrasikan Babel semudah:
$ jscs . --esnext
Dari cli, atau menambahkan opsi `esnext` ke `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
Untuk informasi lebih lanjut lihat dokumentasi [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) atau [`jscs`](http://jscs.info).
### Dokumentasi
Menggunakan Babel, ES2015, dan aliran Anda dapat menyimpulkan banyak tentang kode Anda. Menggunakan [documentation.js](http://documentation.js.org) Anda dapat menghasilkan rinci dokumentasi API sangat mudah.
Documentation.js menggunakan Babel di belakang layar untuk mendukung semua sintaks terbaru termasuk aliran anotasi untuk menyatakan jenis dalam kode Anda.
## Kerangka kerja
Semua kerangka JavaScript utama sekarang berfokus pada penyelarasan api mereka di sekitar masa depan bahasa. Karena ini, ada banyak pekerjaan yang masuk ke perkakas.
Kerangka kerja memiliki kesempatan tidak hanya untuk menggunakan Babel tetapi untuk memperluas cara-cara yang meningkatkan pengalaman pengguna mereka.
### React
Bereaksi telah berubah secara dramatis API untuk menyelaraskan dengan kelas ES2015 ([Baca tentang API yang diperbarui di sini](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Lebih jauh, bereaksi bergantung pada Babel untuk mengkompilasi sintaks BEJ, mencela itu sendiri kustom tooling mendukung Babel. Anda dapat memulai dengan menyiapkan paket `babel-preset-react` mengikuti [petunjuk di atas](#babel-preset-react).
Masyarakat React mengambil Babel dan berlari dengan itu. Sekarang ada sejumlah mentransformasi [dibangun oleh komunitas](https://www.npmjs.com/search?q=babel-plugin+react).
Terutama [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin yang dikombinasikan dengan jumlah [tertentu bereaksi mengubah](https://github.com/gaearon/babel-plugin-react-transform#transforms) dapat mengaktifkan hal-hal seperti *panas modul reload* dan utilitas debugging lain.
## Teks editor dan ide
Memperkenalkan ES2015, BEJ dan aliran sintaks dengan Babel dapat membantu, tapi jika editor teks Anda tidak mendukung itu maka dapat menjadi pengalaman yang benar-benar buruk. Untuk alasan ini, Anda akan ingin untuk men-setup editor teks atau IDE Anda dengan Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Dukungan Babel
Babel memiliki komunitas yang sangat besar dan dengan cepat berkembang, seperti yang kita tumbuh kami ingin memastikan bahwa orang memiliki semua sumber daya yang mereka butuhkan untuk menjadi sukses. Jadi kami menyediakan sejumlah saluran yang berbeda untuk mendapatkan dukungan.
Ingat bahwa seluruh komunitas ini, kami melaksanakan [Kode etik](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). Jika Anda melanggar kode etik, tindakan akan diambil. Jadi silakan membacanya dan menjadi sadar akan hal itu ketika berinteraksi dengan orang lain.
Kami juga mencari untuk tumbuh Komunitas mandiri, untuk orang yang bertahan dan mendukung orang lain. Jika Anda menemukan seseorang mengajukan pertanyaan Anda tahu jawaban, mengambil beberapa menit dan membantu mereka keluar. Cobalah yang terbaik untuk jenis dan pengertian ketika melakukannya.
## Forum Babel
[Wacana](http://www.discourse.org) telah menyediakan software forum gratis untuk kita dengan versi hosted (dan kita mencintai mereka untuk itu!). Jika Forum hal Anda silahkan mampir [discuss.babeljs.io](https://discuss.babeljs.io).
## Chat Babel
Semua orang menyukai [kendur](https://slack.com). Jika Anda sedang mencari dukungan langsung dari masyarakat kemudian datang chatting dengan kami di [slack.babeljs.io](https://slack.babeljs.io).
## Masalah Babel
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
Jika Anda ingin membuka masalah baru:
* [Mencari masalah yang ada](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Membuat laporan bug Babel yang mengagumkan
Babel masalah kadang-kadang bisa sangat sulit untuk debug jarak jauh, jadi kita perlu semua bantuan yang bisa kita dapatkan. Menghabiskan beberapa menit menyusun laporan bug yang benar-benar baik dapat membantu mendapatkan masalah Anda terpecahkan secara signifikan lebih cepat.
Pertama, coba mengisolasi masalah Anda. Hal ini sangat tidak mungkin bahwa setiap bagian dari Anda setup memberikan kontribusi kepada masalah. Jika masalah Anda sepotong masukan kode, mencoba menghapus kode sebanyak mungkin yang masih menyebabkan masalah.
> [WIP]
* * *
> ***Untuk pembaruan terbaru, ikuti [@thejameskyle](https://twitter.com/thejameskyle) di Twitter.***
================================================
FILE: translations/it/README.md
================================================
# Manuale di Babel
Questo manuale è diviso in due parti:
* [Manuale dell'utente](user-handbook.md) - Installazione/configurazione di Babel ed altro.
* [Manuale dei plugins](plugin-handbook.md) - Come creare plugins per Babel.
> Per aggiornamenti futuri, segui [@thejameskyle](https://twitter.com/thejameskyle) su Twitter.
Nel caso in cui stiate leggendo questo documento in una lingua differente dall'inglese, potreste trovare sezioni in inglese ancora in attesa di traduzione. Se siete interessati a contribuire nella traduzione di questo documento, dovrete farlo attraverso Crowdin. Per maggiori informazioni, leggere la sezione [linee guida di contribuzione](/CONTRIBUTING.md). Troverete una serie di parole inglesi che sono concetti di programmazione. Se queste parole fossero tradotte in altre lingue, ci sarebbe una mancanza di coerenza e fluidità durante la lettura. In molti casi troverete la traduzione letterale, seguita dal termine inglese tra parentesi `()`. Ad esempio: Alberi Sintattici Astratti (ASTs).
================================================
FILE: translations/it/plugin-handbook.md
================================================
# Babel Plugin Handbook
Questo documento include le linee guida per la creazione di [plugins](https://babeljs.io/docs/advanced/plugins/) per [Babel](https://babeljs.io).
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Sommario
* [Introduzione](#toc-introduction)
* [Nozioni di base](#toc-basics)
* [AST](#toc-asts)
* [Stadi primari di Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Analisi Lessicale](#toc-lexical-analysis)
* [Analisi Sintattica](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Navigazione (traverse)](#toc-traversal)
* [Visitatori (visitor)](#toc-visitors)
* [Percorsi (path)](#toc-paths)
* [Oggetto "path" disponibile nei visitatori](#toc-paths-in-visitors)
* [Stato (state)](#toc-state)
* [Scope](#toc-scopes)
* [Binding](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definizioni](#toc-definitions)
* [Costruttori](#toc-builders)
* [Validatori](#toc-validators)
* [Convertitori](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Scrivere il primo plugin per Babel](#toc-writing-your-first-babel-plugin)
* [Operazioni di trasformazione](#toc-transformation-operations)
* [Navigazione AST](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipolazione AST](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope (e variabili)](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Opzioni per i propri plugin](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Generare nodi (per inserimenti e trasformazioni)](#toc-building-nodes)
* [Best practice](#toc-best-practices)
* [Evitare navigazioni non necessarie dell'AST](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Aggregare i visitatori quando possibile](#toc-merge-visitors-whenever-possible)
* [Evitare navigazioni programmatiche nidificate](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Ottimizzare i visitatori nidificati](#toc-optimizing-nested-visitors)
* [Essere consapevoli delle strutture nidificate](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introduzione
Babel è un compilatore multiuso e generalista per JavaScript. Nello specifico è un insieme di moduli che possono essere usati per differenti operazioni di analisi statica del codice.
> Per analisi statica si intende il processo di analisi di codice senza che questo venga eseguito. (L'analisi del codice effettuata durante la sua esecuzioni è definita analisi dinamica). Le finalità dell'analisi statica possono essere moteplici. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
È possibile utilizzare Babel per costruire diversi tipi di strumenti che consentono di essere più produttivi e scrivere programmi migliori.
> ***Per aggiornamenti futuri, segui [@thejameskyle](https://twitter.com/thejameskyle) su Twitter.***
* * *
# Nozioni di base
Babel è un compilatore JavaScript, specificamente un compilatore di fonte a fonte, spesso chiamato un "transpiler". Ciò significa che puoi dare Babel del codice JavaScript che viene modificato da Babel, il quale genera del nuovo codice.
## AST
Ognuno di questi passaggi comportano la creazione o l'utilizzo con un [Albero Sintattico Astratto](https://en.wikipedia.org/wiki/Abstract_syntax_tree) o AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Oppure come un oggetto JavaScript come questo:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Noterete che ogni livello di un AST ha una struttura simile:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Nota: Alcune proprietà sono state rimosse per semplicità.
Ognuno di questi è conosciuto come un **Nodo**. Un AST può essere costituito da un singolo nodo, o centinaia se non migliaia di nodi. Insieme, essi sono in grado di descrivere la sintassi di un programma che può essere utilizzato per l'analisi statica.
Ogni Nodo ha questa interfaccia:
```typescript
interface Node {
type: string;
}
```
La proprietà `type` è una stringa che rappresenta il tipo di oggetto del Nodo (ie. `"FunctionDeclaration"`, `"Identificatore"` o `"BinaryExpression"`). Ogni tipo di Nodo definisce un ulteriore insieme di proprietà che descrivono il tipo di Nodo in particolare.
Ci sono altre proprietà su ogni nodo che Babel genera che descrivono la posizione del nodo nel codice sorgente originale.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Queste proprietà `start`, `end`, `loc`, appaiono in ogni singolo Nodo.
## Stadi primari di Babel
Le tre fasi primarie di Babel sono **parse**, **trasformare**, **generare**.
### Parse
Lo stage di **parse** ha come input del codice e come output un AST. Ci sono due fasi di analisi in Babel: [**Analisi Lessicale**](https://en.wikipedia.org/wiki/Lexical_analysis) e [**Analisi Sintattica**](https://en.wikipedia.org/wiki/Parsing).
#### Analisi Lessicale
Durante l'analisi lessicale, Babel analizza una stringa di codice e la trasforma in una serie di **tokens**.
Si può pensare ai tokens come un vettore che contiene pezzi di sintassi del linguaggio.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Ciascun `type` ha un insieme di proprietà che descrivono il token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
In modo simile ai Nodi AST, anche i tokens hanno le proprietà `start`, `end` e `loc`.
#### Analisi Sintattica
L'analisi sintattica prende una serie di tokens e costruisce una rappresentazione AST. Utilizzando le informazioni nei token, questa fase li riformatterà come un AST che rappresenta la struttura del codice.
### Transform
La fase di [trasformazione](https://en.wikipedia.org/wiki/Program_transformation) prende un AST e lo attraversa aggiungendo, modificando e rimuovendo Nodi durante l'attraversamento. Questa è di gran lunga la parte più complessa di Babel o qualsiasi altro compilatore. Questa fase è dove operano i plugin ed è quindi l'argomento principale trattato in questo manuale. Quindi non andremo in profondo in questa fase in questa sezione.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Navigazione (traverse)
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Per esempio prendiamo un nodo di tipo `FunctionDeclaration`. Ha diverse proprietà: `id`, `params` e `body`. Ognuno di essi ha uno o più nodi nidificati.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Così iniziamo dalla `FunctionDeclaration` e conosciamo le proprietà interne quindi visitiamo ciascuno di essi e i loro figli in ordine.
La proprietà successiva è `id` è un `identificatore`. L'`identificatore ` non ha altri nodi nidificati quindi andiamo avanti.
Successivamente troviamo la proprietà `params`, ovvero un vettore di nodi. Visitiamo ciascuno di essi. In questo caso è un singolo nodo che è anche un `identificatore`, quindi andiamo avanti.
Adesso arriviamo alla proprietà `body` che è un `BlockStatement` con una proprietà `body` che è un vettore di nodi, quindi andiamo a ciascuno di essi.
Qui l'unico elemento è un nodo di `ReturnStatement` che ha un `argument`, andiamo all' `argument` e troviamo una `BinaryExpression`.
L'oggetto `BinaryExpression` ha le proprietà `operator`, `left` e `right`. L'operator non è un nodo ma solo un valore - "*", quindi visitiamo le proprietà `left` e `right`.
Questo processo di attraversamento dell'AST avviene durante lo stage di trasformazione di Babel.
### Visitatori (visitor)
Quando si parla di "andare" a un nodo, intendiamo in realtà la **visita** di un nodo. La ragione per che cui usiamo quel termine è perché è sfruttato il concetto di [**visitatore**](https://en.wikipedia.org/wiki/Visitor_pattern).
I visitatori sono un modello utilizzato in attraversamento di AST tra i diversi linguaggi. In poche parole sono un oggetto con metodi definiti per accettare tipi di nodi particolari in un albero. È un po' astratto, quindi diamo un'occhiata a un esempio.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Nota:** `Identifier() { ... }` è l'abbreviazione di `Identifier: {enter () { ... }}`.
Questo è un visitatore base che quando utilizzato durante un attraversamento chiamerà il metodo `Identifier()` per ogni `identifier` nell'albero.
Per esempio con questo codice il metodo `Identifier()` verrà chiamato quattro volte con ogni `identificatore` (tra cui `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
Queste chiamate sono tutte eseguite mentre si sta per**entrare** nel nodo. Però c'è anche la possibilità di eseguire un metodo di un visitatore durante **l'uscita** dal nodo.
Immaginate di avere questa struttura ad albero:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
Durante l'attraversamento di ogni ramo dell'albero, eventualmente raggiungiamo dei nodi senza altri rami, quindi per arrivare al nodo successivo c'è bisogno di ritornare in su per arrivare al nodo successivo. Andando verso il basso l'albero **entriamo** ciascun nodo, quindi tornando indietro **usciamo** da ogni nodo.
Adesso andiamo *passo per passo* attraverso il processo che viene eseguito per l'AST descritto in precedenza.
* Entra nella `FunctionDeclaration`
* Entra nell' `Identifier (id)`
* Vicolo cieco (non ci sono ulteriori nodi dato che id ha un solo valore)
* Esci dall' `Identifier(id)`
* Entra nell'`Identifier (params[0])`
* Vicolo cieco (non ci sono ulteriori nodi dato che id ha un solo valore)
* Esci dall'`Identifier (params[0])`
* Entra nel `BlockStatement (body)`
* Entra nella `ReturnStatement (body)`
* Entra nella `BinaryExpression (argument)`
* Entra nella `Identifier (left)`
* Vicolo cieco (non ci sono ulteriori nodi dato che id ha un solo valore)
* Esci dall'`Identifier (left)`
* Entra nella `Identifier (right)`
* Vicolo cieco (non ci sono ulteriori nodi dato che id ha un solo valore)
* Esci dall'`Identifier (right)`
* Esci dalla `BinaryExpression (argument)`
* Esci dal `ReturnStatement (body)`
* Esci dal `BlockStatement (body)`
* Esci dalla `FunctionDeclaration`
Quindi durante la creazione di un visitatore hai due opportunità per visitare il nodo.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Percorsi (path)
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Oggetto "path" disponibile nei visitatori
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Stato (state)
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scope
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Binding
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definizioni
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Costruttori
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validatori
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Convertitori
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Scrivere il primo plugin per Babel
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Operazioni di trasformazione
## Navigazione AST
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipolazione AST
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope (e variabili)
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Opzioni per i propri plugin
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Generare nodi (per inserimenti e trasformazioni)
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best practice
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Evitare navigazioni non necessarie dell'AST
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Aggregare i visitatori quando possibile
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Evitare navigazioni programmatiche nidificate
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Ottimizzare i visitatori nidificati
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Essere consapevoli delle strutture nidificate
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/it/user-handbook.md
================================================
# Manuale utente di Babel
This document covers everything you ever wanted to know about using [Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
Questo manuale è disponibile in altre lingue, vedere il [README](/README.md) per un elenco completo.
# Sommario
* [Introduzione](#toc-introduction)
* [Installazione di Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Introduzione
Babel è un compilatore multifunzione per JavaScript. Utilizzando Babel è possibile utilizzare (e creare) la prossima generazione di JavaScript, così come la prossima generazione di strumenti JavaScript.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***Per aggiornamenti futuri, segui [@thejameskyle](https://twitter.com/thejameskyle) su Twitter.***
* * *
# Installazione di Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***Per aggiornamenti futuri, segui [@thejameskyle](https://twitter.com/thejameskyle) su Twitter.***
================================================
FILE: translations/ja/README.md
================================================
# Babel Handbook
このハンドブックは2つの資料に分かれています。
* [User Handbook](user-handbook.md) - Babel等のセットアップと設定について
* [Plugin Handbook](plugin-handbook.md) - Babelのプラグイン作成について
> 最新の情報を受け取るには、Twitterで[@thejameskyle](https://twitter.com/thejameskyle)をフォローしてください。
英語以外の翻訳版ハンドブックには、まだ翻訳されていない英語の文章があるかもしれません。 もし翻訳プロジェクトに参加いただける場合には、Crowdinを利用してください。 より詳しい情報は[貢献のためのガイドライン](/CONTRIBUTING.md)を参照してください。 あなたはプログラミング概念の中で多くの英単語を見るでしょう。 これらの英単語は、他の言語に翻訳してしまうと文章の一貫性と流暢さが損なわれる事となります。 多くの場合は逐語訳の後に`()`の中に元の英単語が書いてあります。 例えば:抽象構文木(ASTs)
================================================
FILE: translations/ja/plugin-handbook.md
================================================
# Babel Plugin Handbook
このドキュメントでは[Babel](https://babeljs.io)の[プラグイン](https://babeljs.io/docs/advanced/plugins/)を作る方法を解説します。.
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# 目次
* [イントロダクション](#toc-introduction)
* [基本](#toc-basics)
* [抽象構文木(ASTs)](#toc-asts)
* [バベルの現状](#toc-stages-of-babel)
* [パーサー](#toc-parse)
* [字句解析](#toc-lexical-analysis)
* [構文解析](#toc-syntactic-analysis)
* [変換](#toc-transform)
* [ジェネレーター](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Writing your first Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [スコープ](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [プラグインのオプション](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [ベストプラクティス](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# イントロダクション
BabelはJavaScriptのための汎用的で多目的に使用できるコンパイラです。また、様々な静的コード解析に利用するためのモジュールのコレクションでもあります。
> 静的コード解析とは、実行すること無くコードの分析を行うプロセスです。 (コードの実行中にそれを分析するのは動的コード解析と呼ばれます。) 静的コード解析の目的は様々です。 Lint、コンパイル、コードハイライト、トランスフォーム、最適化、縮小など、様々な目的で利用することができます。
Babelを利用することで、より生産的で、より良いコードを書くためのツールを作ることができます。
> ***最新の情報を受け取るには、Twitterで[@thejameskyle](https://twitter.com/thejameskyle)をフォローしてください。***
* * *
# 基本
BabelはJavaScriptのコンパイラ、特にソースからソースへ変換する「トランスパイラ(Transpiler)」と呼ばれる種類のコンパイラです。 つまり、BabelにJavaScriptのコードを与えることで、Babelはコードを変更し新しいコードを生成します。
## 抽象構文木(ASTs)
コードの変換の各ステップでは[抽象構文木(ASTs)](https://en.wikipedia.org/wiki/Abstract_syntax_tree)またはASTを利用します。
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> ASTノードについて理解を深めたい場合は[AST Explorer](http://astexplorer.net/)を使ってみてください。 上記のサンプルコードの例は[ここ](http://astexplorer.net/#/Z1exs6BWMq)で確認することができます。
上記のサンプルコードをASTノードとして表すと以下の様になります。
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
またはJavaScriptのオブジェクトして表現すると、以下の様になります。
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
ASTの各階層は同じような構造をしていることに気付くでしょう。
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> 注) いくつかのプロパティは、単純化のため省略しています。
これらは**ノード(Node)**と呼ばれます。 ASTは単一のノード、または何百、何千のノードから構成することができます。 これらを利用し、静的コード解析に利用するプログラムの文法を説明することができるのです。
全てのノードはインターフェイスを持ちます。
```typescript
interface Node {
type: string;
}
```
`type`フィールドは、オブジェクトのノードの型を表す文字列です(例えば、 `"FunctionDeclaration"`、`"Identifier"`、`"BinaryExpression"`などがあります。) ノードの種類は特定のノードの型を記述するためのプロパティのセットを追加して定義します。
Babelが生成したノードには、元のソースコード上のノードの位置を記述した追加のプロパティがセットされます。
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
これらのプロパティには`start`、`end`、`loc`が一つのノードに出現します。
## Babelのステージ
Babelには大きく分けて3つのステージが存在します。すなわち、**parse**、**transform**、そして**generate**です。.
### パーサー
**parse**は、コードを入力として受け取り、ASTを出力するステージです。 さらに、parseは2つのフェーズに分けることができます。すなわち、 [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) と [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing)です。.
#### 字句解析
Lexical Analysisは、コードの文字列を**token**のストリームへ変換するフェーズを指します。.
tokenは言語の構文の個々の部品であり、tokenのストリームはそれらがフラットに並んだものと考えてください。
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
上記はtokenのストリームですが、それぞれのtokenは`type`を持ち、それは以下の様なプロパティから構成されています。
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
ASTのノードと同様、typeもまた`start`、`end`、`loc`といったプロパティを持ちます。.
#### 構文解析
一方、Syntactic Analysisは、tokenのストリームをASTに変換するフェーズを指します。 ここでは、tokenの情報をベースにそれらを再構成して、コードの構造をより加工しやすい形(AST)で表現します。
### 変換
[transform](https://en.wikipedia.org/wiki/Program_transformation) ステージでは、ASTのツリーを走査して、ノードの追加、変更、削除といった処理を施します。 このステージこそが最も複雑なステージであり、それはBabelのみならず、他のコンパイラにおいても同様です。 また、このステージこそがプラグインに関わる部分であるため、言わばこのハンドブックの大半はtransformに関して書かれています。 したがって、ここでは簡単に説明するだけに留めたいと思います。
### ジェネレーター
[generate](https://en.wikipedia.org/wiki/Code_generation_(compiler))(code generation)ステージは、ASTをふたたびコードの文字列に変換するステージです。さらに、このステージは[source map](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/)も生成します。.
code generationの処理は単純明快です。それは、ASTのツリーをdepth-firstの順番で走査することで、変換結果としてのコードの文字列を構築します。
## Traversal
transformのステージでは、ASTのツリーを再帰的に走査(traverse)する必要があります。
たとえば、typeが`FunctionDeclaration`のASTがあるとしましょう。このASTは `id`、`params`、そして`body`という3つのネストしたノードを含みます。
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
ご覧のとおり、`FunctionDeclaration`以下のノードはさらに子ノードを持つため、それらひとつひとつを走査しなければいけません。
まずは、`id`を見てみましょう。これは`Identifier`ノードであり、自身の直接の子ノードのみを持ちます。
次に、`params`を見てみましょう。これはノードの配列で、それぞれのノードはまたしても、`Identifier`です。
最後に`body` を見てみましょう。これは`BlockStatement` であり、`body`というプロパティを持ちます。<0>body0>はノードの配列なので、ひとつづつ辿ってみましょう。
配列は単一のノード(`ReturnStatement`)から構成されており、`argument`というプロパティを持ちます。`argument`は`BinaryExpression`であり、さらに子ノードを持ちます。.
`BinaryExpression` は`operator`、`left`、そして`right`の3つのプロパティを持ちます。 operatorはノードではなく、ただの値です。一方、`left`と`right`はノードです。.
この操作(traversal)のプロセスは、Babelのtransformステージ全体を通じて行われます。
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Writing your first Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## スコープ
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# プラグインのオプション
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# ベストプラクティス
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/ja/user-handbook.md
================================================
# Babel User Handbook
このドキュメントはあなたが知りたい[Babel](https://babeljs.io)やその関連ツールの使い方をカバーします。
[](http://creativecommons.org/licenses/by/4.0/)
このハンドブックは他の言語にも翻訳されています。全ての言語のリストについては[README](/README.md)を参照してください。
# 目次
* [イントロダクション](#toc-introduction)
* [Babel のセットアップ](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [プロジェクトでBabel CLI を実行する](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Babel の設定](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Babelで生成したコードを実行する](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Babel 設定 (高度な)](#toc-configuring-babel-advanced)
* [プラグインを手動で設定](#toc-manually-specifying-plugins)
* [プラグインのオプション](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [静的解析ツール](#toc-static-analysis-tools)
* [Lint](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [フレームワーク](#toc-frameworks)
* [React](#toc-react)
* [テキストエディタ・IDE](#toc-text-editors-and-ides)
* [Babel のサポート](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# イントロダクション
BabelはJavaScriptのための包括的な多目的コンパイラです。Babelを利用すると、あなたは次世代のJavaScriptを体験するだけではなく、次世代JavaScript用ツールを利用(または作成)することが出来ます。
Javascripは常に新しい標準規格や提案などによって新機能が実装され続けており、常に進化している言語です。 Babelを使うことによって旧環境でも最新の機能を使えるようになります。
これを実現するためにBabelは最新の規格で書かれたJavascriptを、現在標準的に対応されているバージョンにコンパイルします。 このコンパイルは、source-to-sourceコンパイル、もしくはトランスパイルと呼ばれます。
例えば、Babelは新しいES2015規格のアロー関数シンタックスをこれ:
```js
const square = n => n * n;
```
から以下のコードに変換します:
```js
const square = function square(n) {
return n * n;
};
```
しかし、それだけではありませんBabelは様々な文法に対応しており、ReactのJSXシンタックスやFlowの静的型付けにも対応しています。
さらに、Babelの全てはプラグインになっており、誰でも自由にBabelをフル活用したプラグインを開発できます。
*さらにさらに*、Babelは何個かのコアモジュールに分割されており、次世代のJavascriptツール開発に使えます。
Babelの開発者コミュニティは広く大きく、上記のリソースをフル活用しています。 このハンドブックの中ではBabelの組み込みツールの使い方と、開発者コミュニティによって作られた便利なツールも取りあげていきます。
> ***最新の情報を受け取るには、Twitterで[@thejameskyle](https://twitter.com/thejameskyle)をフォローしてください。***
* * *
# Babel のセットアップ
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** ここでは`node`や`npm`といったコマンドラインツールの使い方を紹介しません。これらのツールに習熟していることを前提とします。
## `babel-cli`
Babel CLIによって、コマンドラインから Babel で簡単にコンパイルできます。
グローバルインストールして、基本を学びましょう。
```sh
$ npm install --global babel-cli
```
初めてコンパイルするにはこんな感じにします。
```sh
$ babel my-file.js
```
コンパイルした結果がターミナルにそのままダンプされます。ファイルに書き込む際は`--out-file` or `-o`オプションを指定してください。.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
ディレクトリ全体をコンパイルしたい場合は`--out-dir` or `-d`オプションを指定してください.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### プロジェクトでBabel CLI を実行する
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
`index.js`ファイルをプロジェクトに作成しましょう。
```js
console.log("Hello world!");
```
仮に`node index.js`を実行していたら、コンパイルされません。代わりに`babel-register`をセットアップしましょう。.
初めに、`babel-register`をインストールします.
```sh
$ npm install --save-dev babel-register
```
次に、プロジェクトに`register.js`ファイルを生成し、以下のコードを記述します。
```js
require("babel-register");
require("./index.js");
```
こうして、Babelをノードのモジュールシステムに登録(*register*) し、 `require`した全ファイルのコンパイルを開始します。
それでは `node index.js` を実行して ` register.js` を使用してみましょう。
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Babel の設定
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Babelで生成したコードを実行する
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Babel 設定 (高度な)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## プラグインを手動で設定
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## プラグインのオプション
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## 静的解析ツール
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Lint
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
初めに、`eslint` と`babel-eslint`をインストールします.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## フレームワーク
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## テキストエディタ・IDE
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel のサポート
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***最新の情報を受け取るには、Twitterで[@thejameskyle](https://twitter.com/thejameskyle)をフォローしてください。***
================================================
FILE: translations/ko/README.md
================================================
# Babel Handbook
이 핸드북은 두 파트로 분리되어 있습니다:
* [User Handbook](user-handbook.md) - Babel을 설치/운용 하는 방법과 여러가지
* [Plugin Handbook](plugin-handbook.md) - Babel 플러그인을 만드는 방법
> 향후 업데이트에 대한 내용은 Twitter의 [@thejameskyle](https://twitter.com/thejameskyle)를 팔로우하세요.
만약 영문 문서가 아닌 번역된 핸드북을 읽고 있다면, 여전히 번역되지 않은 영문 문서 섹션을 찾아 읽을 수 있습니다. 만약 다른 언어로 문서를 번역하고 싶다면 Crowdin을 통해 기여해야 합니다. 자세한 사항은 [기여 가이드](/CONTRIBUTING.md)를 참고하세요. 문서에서 프로그래밍의 개념적인 영문 단어들을 몇가지 찾을 수 있습니다. 이 문서는 번역된 문서이기 때문에 문서를 읽을 때 원문과 비교했을 때 일관성과 표현이 부족할 수 있습니다. 따라서 영문 그대로의 문장 또는 단어를 `()`에 그대로 넣은 경우를 많이 볼 수 있습니다. Abstract Syntax Trees (ASTs) 를 예시로 들 수 있습니다.
================================================
FILE: translations/ko/plugin-handbook.md
================================================
# Babel Plugin Handbook
이 문서는 [Babel](https://babeljs.io) [플러그인](https://babeljs.io/docs/advanced/plugins/)을 만드는 방법을 설명합니다.
[](http://creativecommons.org/licenses/by/4.0/)
이 핸드북은 다른 언어로도 볼 수 있습니다. 전체 목록은 [README](/README.md)를 참고하세요.
# 목차
* [소개](#toc-introduction)
* [기본](#toc-basics)
* [추상 구문 트리 (ASTs)](#toc-asts)
* [Babel 실행 단계](#toc-stages-of-babel)
* [분석(Parse)](#toc-parse)
* [어휘 분석(Lexical Analysis)](#toc-lexical-analysis)
* [구문 분석(Syntactic Analysis)](#toc-syntactic-analysis)
* [변환(Transform)](#toc-transform)
* [생성(Generate)](#toc-generate)
* [탐색(Traversal)](#toc-traversal)
* [방문자(Visitors)](#toc-visitors)
* [경로(Paths)](#toc-paths)
* [방문자안의 경로(Paths in Visitors)](#toc-paths-in-visitors)
* [상태(State)](#toc-state)
* [범위(Scopes)](#toc-scopes)
* [묶기(Binding)](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [정의(Definitions)](#toc-definitions)
* [빌더(Builders)](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [첫 Babel 플러그인 작성](#toc-writing-your-first-babel-plugin)
* [변환 작업](#toc-transformation-operations)
* [방문하기(Visiting)](#toc-visiting)
* [서브-노드의 경로 얻기](#toc-get-the-path-of-a-sub-node)
* [노드가 어떤 형식인지 확인하기](#toc-check-if-a-node-is-a-certain-type)
* [경로가 어떤 형식인지 확인하기](#toc-check-if-a-path-is-a-certain-type)
* [식별자가 참조되었는지 확인하기](#toc-check-if-an-identifier-is-referenced)
* [특정한 부모 경로 찾기](#toc-find-a-specific-parent-path)
* [형제 경로 얻기](#toc-get-sibling-paths)
* [탐색 중지하기](#toc-stopping-traversal)
* [노드 변형하기](#toc-manipulation)
* [노드 교체하기](#toc-replacing-a-node)
* [노드 하나를 여러 개의 노드로 교체하기](#toc-replacing-a-node-with-multiple-nodes)
* [노드 하나를 소스 문자열로 교체하기](#toc-replacing-a-node-with-a-source-string)
* [형제 노드를 삽입하기](#toc-inserting-a-sibling-node)
* [컨테이너에 삽입하기](#toc-inserting-into-a-container)
* [노드 삭제하기](#toc-removing-a-node)
* [부모 교체하기](#toc-replacing-a-parent)
* [부모 삭제하기](#toc-removing-a-parent)
* [스코프](#toc-scope)
* [지역 변수가 바인딩되었는지 확인하기](#toc-checking-if-a-local-variable-is-bound)
* [UID 생성하기](#toc-generating-a-uid)
* [부모 스코프에 변수 선언 집어넣기](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [바인딩과 참조의 이름 바꾸기](#toc-rename-a-binding-and-its-references)
* [플러그인 옵션](#toc-plugin-options)
* [플러그인의 전처리와 후처리](#toc-pre-and-post-in-plugins)
* [플러그인에서 구문 활성화하기](#toc-enabling-syntax-in-plugins)
* [노드 만들기(Building Nodes)](#toc-building-nodes)
* [모범 사례](#toc-best-practices)
* [가능한 AST 탐색을 피하라](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [가능하다면 방문자들을 병합(merge) 하라](#toc-merge-visitors-whenever-possible)
* [수동 조회할 땐 탐색하지 마라](#toc-do-not-traverse-when-manual-lookup-will-do)
* [중첩된 방문자들 최적화하기](#toc-optimizing-nested-visitors)
* [중첩된 구조(nested structures) 인지하기](#toc-being-aware-of-nested-structures)
* [단위 테스트](#toc-unit-testing)
# 소개
Babel은 일반적으로 다목적 자바스크립트 컴파일러입니다. 더 나아가 많은 형태의 정적 분석 (Static analysis) 에 사용되는 모듈들의 모음(collection) 이기도 합니다.
> 정적 분석(Static analysis) 이란 코드를 실행하지 않고 분석하는 작업 입니다. 코드를 실행하는 동안 분석하는 것은 동적 분석(dynamic analysis) 으로 알려져 있습니다. 정적 분석의 목적은 매우 다양합니다. 구문 검사 (linting), 컴파일링, 코드 하이라이팅, 코드 변환, 최적화, 압축(minification) 등 매우 많은 용도로 사용될 수 있습니다.
생산성을 향상시켜주고 더 좋은 프로그램을 작성하게 해주는 많은 다양한 형태의 도구들을 Babel 을 사용하여 만들 수 있습니다.
> ***향후 업데이트에 대한 내용은 Twitter의 [@thejameskyle](https://twitter.com/thejameskyle)를 팔로우하세요.***
* * *
# 기본
Babel은 JavaScript 컴파일러입니다. 정확히는 source-to-source 컴파일러이며 "트랜스파일러(transpiler)" 라고 불립니다. 즉, Babel에 자바스크립트 코드를 넘겨주면 Babel에서 코드를 수정하고 새로운 코드를 생성하여 반환해주게 됩니다.
## 추상 구문 트리 (ASTs)
각 과정들은 [추상 구문 트리(AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree)를 생성하거나 이것을 다루게됩니다.
> Babel은 [ESTree](https://github.com/estree/estree)에서 수정된 AST를 사용하며, 핵심 스팩은 [여기](https://github.com/babel/babylon/blob/master/ast/spec.md)에 있습니다.
```js
function square(n) {
return n * n;
}
```
> AST 노드(AST nodes) 들에 더 알고 싶으면 [AST 탐색기](http://astexplorer.net/)를 확인해보세요. [이 링크](http://astexplorer.net/#/Z1exs6BWMq) 는 위 코드를 붙여넣기한 예제입니다.
위의 코드는 이런 트리로 나타낼 수 있습니다:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
또는 이런 자바스크립트 객체로 나타낼 수도 있습니다.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
AST 의 각 레벨들이 유사한 구조를 가지고 있다는것을 알 수 있습니다.
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> 주의: 간단하게 보기 위해 몇개의 속성은 제거하였음.
이것들을 각각 **노드(Node)** 라고 합니다. AST 는 단일 노드 또는 수백개, 수천개의 노드로 이루어 질수 있습니다. 이 노드들이 모여 프로그램의 구문(syntax) 을 설명 할 수 있는데 이것은 정적 분석에 사용될 수 있습니다.
모든 노드는 이런 인터페이스를 가지고 있습니다:
```typescript
interface Node {
type: string;
}
```
`type` 필드는 객체가 어떤 노드 타입인지 나타내는 문자열입니다. (예. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). 각 노드 type 은 이 특정 노드 type 을 기술하는 추가 속성들의 집합을 정의합니다.
Babel 이 만드는 모든 노드에는 오리지널 소스코드안의 노드의 위치를 알려주는 추가 속성이 있습니다.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
이 `start`, `end`, `loc` 속성들은 모든 단일 노드안에 있습니다.
## Babel 실행 단계
바벨 과정의 주요 3단계는 **분석(parse)**, **변환(transform)**, **생성(generate)** 단계입니다..
### 분석(Parse)
**분석** 단계에서는, 코드는 취해 AST를 만들어냅니다. 바벨의 분석단계는 [**어휘 분석(Lexical Analysis)**](https://en.wikipedia.org/wiki/Lexical_analysis) 과 [**구문 분석(Syntactic Analysis)**](https://en.wikipedia.org/wiki/Parsing) 두 가지 단계가 있습니다..
#### 어휘 분석(Lexical Analysis)
어휘 분석 과정은 코드 문자열을 취해 **토큰(tokens)**들의 스트림(stream) 으로 바꿀 것 입니다..
토큰들을 언어 구문 조각의 플랫한 배열이라고 생각해도됩니다.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
여기 각 `type`들은 이 토큰을 기술하는 속성들의 집합을 가지고 있습니다:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
이것은 또한 AST 노드처럼 `start`, `end`, 그리고 `loc` 을 가지고 있습니다..
#### 구문 분석(Syntactic Analysis)
구문 분석 과정에서는 토큰들의 스트림을 취해 AST 표현으로 바꿀 것 입니다. 이 과정에서는 토큰들 안에 있는 정보를 사용해서, 토큰들을 코드의 구조를 나타내는 AST로 재 구성하여 다루기 쉽게 만듭니다.
### 변환(Transform)
[변환](https://en.wikipedia.org/wiki/Program_transformation) 단계에서는 추상 구문 트리(AST) 를 받아 그 속을 탐색해 나가며 노드들을 추가, 업데이트, 제거 합니다. Babel 이나 어떤 컴파일러에게라도 이 과정은 단연코 가장 복잡한 부분입니다. 이 과정에서 플러그인이 수행되므로 이 핸드북에서 가장 많이 다뤄질 것입니다. 그래서 지금 당장은 너무 깊게 들어가지 않겠습니다.
### 생성(Generate)
[코드 생성](https://en.wikipedia.org/wiki/Code_generation_(compiler)) 단계에서는 최종 AST를 취하여 다시 소스 코드 문자열로 만드는데, [소스 맵](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/) 또한 생성합니다..
소스 코드 생성은 매우 간단합니다: AST를 깊이 우선 탐색법으로 탐색하며 변환된 코드를 나타내는 문자열을 만듭니다.
## 탐색(Traversal)
AST 변환을 하고 싶을때, 재귀적으로 [트리를 탐색](https://en.wikipedia.org/wiki/Tree_traversal)해야합니다.
`FunctionDeclaration` type이 있다고 해봅시다. 이것은 몇 개의 속성들을 가지고 있습니다: `id`, `params`, 그리고 `body`. 이 각각은 중첩된 노드들을 가지고 있습니다.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
자 이제 `FunctionDeclaration` 부터 시작하고 우린 이것의 내부 속성을 알고있으니 각 속성들과 그들의 자식을 차례로 방문합니다.
다음은 `Identifier` 인 `id` 입니다. `Identifier`는 자식 노드도 갖고 있지 않으므로 넘어갑시다.
다음은 `params` 인데, 이것은 노드들의 배열이므로 각각을 방문합니다. 여기에서는 `Identifier`인 단일 노드뿐이므로 넘어갑시다.
그러면 이제 `body`인데 노드들의 배열인 `body`를 속성으로 가지는 `BlockStatement`이므로 각각의 노드를 방문합니다.
여기엔 오직 `argument` 속성을 가지고 있는 `ReturnStatement` 하나가 있는데, `argument` 속성으로 가면 `BinaryExpression` 이 있습니다.
`BinaryExpression`은 `operator`, `left`, `right` 를 가지고 있습니다. Operator 는 노드가 아닌 단순 값이라 여긴 방문하지 않고, 대신 `left` 와 `right`를 방문 해봅시다.
이런 탐색(traversal) 과정은 바벨 변환(transform) 과정 내내 일어납니다.
### 방문자(Visitors)
어떤 노드로 '간다'라고 말할때, 실제로 이건 노드들을 **방문**한다는 의미입니다. 이 단어를 사용하는 이유는 [**방문자(visitor)**](https://en.wikipedia.org/wiki/Visitor_pattern) 라는 개념이 있기 때문입니다..
방문자는 여러 언어에서 AST 탐색에 사용되는 패턴입니다. 간단히 말해서 그들은 트리에서 특정 노트 타입을 다루기 위한 메소드들을 가지고 있는 객체입니다. 조금 추상적인 내용이라 예를 들어 살펴보겠습니다.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
이것은 탐색을 하는 동안 트리에서 만나는 모든 `Identifier` 에 대해 `Identifier()` 를 호출하는 기본적인 방문자입니다.
자 `Identifier()` 메소드를 가지고 있는 이 코드는 각각의 `Identifier`에 대해 4번 호출될 것입니다(`square`를 포함하여).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
이들은 모두 노드에 **입장(enter)**할때 호출됩니다. 그러나 **퇴장(exit)**할때 방문자 메소드가 호출되게 하는 것도 가능합니다.
이런 구조의 트리를 가지고 있다고 상상해봅시다.
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
트리의 각 가지들을 탐색하는 동안 결국 막다른 곳에 다다르는데 여기에서 다음 노드로 가기위해서는 뒤로 돌아가는 것이 필요합니다. 트리 아래로 내려가면서 각 노드에 **입장(enter)**하고, 돌아올때 각 노드를 **퇴장(exit)** 합니다.
이 과정이 어떻게 되는지 위의 트리로 한번 해봅시다.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
이와같이 방문자를 생성할때 각 노드를 방문할 수 있는 두 번의 기회가 주어집니다.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
만일 필요하다면, 메소드 이름을 문자열로 `Identifier|MemberExpression`와 같이 `|`로 나눔으로써 같은 함수를 여러 방문자 노드에 적용할 수 있습니다.
예시 [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) 플러그인
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
또한 별칭(aliases)을 사용할 수도 있습니다.([babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)에 정의된 대로).
예를 들어,
`Function` 은 `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` 그리고 `ClassMethod` 에 대한 별명입니다..
```js
const MyVisitor = {
Function(path) {}
};
```
### 경로(Paths)
추상구문트리(AST) 는 일반적으로 많은 노드들을 가지고 있는데, 어떻게 노드들이 다른 노드와 연결될 수 있을까요? 어디서든 조작하고 접근할 수 있는 하나의 거대한 변경가능한(mutable) 객체를 가질 수 있지만, **Paths** 를 사용해 단순화시킬 수도 있습니다.
**Path** 는 두 노드 사이의 연결을 표현하는 객체입니다.
예를들어 아래와 같은 노드와 그 자식 노드가 있다고 해봅시다:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
그리고 자식 노드인 `Identifier`를 경로(path)로 표현하면, 다음과 같습니다:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
이것은 또한 경로에 대한 추가적인 메타데이터(metadata)를 가집니다:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
뿐만아니라 노드들의 추가, 업데이트, 이동, 삭제와 관련된 굉장히 많은 메소드들도 있습니다만, 나중에 알아보도록 하겠습니다.
한마디로 Path들은 트리에서 노드의 위치에 대한 **리액티브(reactive)**한 표현이고 노드에 관련된 모든 종류의 정보입니다. 언제든지 트리를 변경하는 메소드를 호출할때마다, 이 정보는 업데이트됩니다. Babel은 당신이 노드들을 쉽고, 최대한 stateless하게 다루도록 이 모든 것을 관리합니다.
#### 방문자 안의 Paths (Paths in Visitors)
`Identifier()` 메소드를 가지는 방문자가 있을때, 실제로 방문하는 것은 노드가 아닌 path 입니다. 따라서 노드를 직접 다루기 보다는 주로 그 노드의 리액티브(reactive)한 표현을 사용하여 작업할 수 있습니다.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### 상태(State)
상태(State) 는 추상 구문 트리(AST) 변환의 적입니다. 상태는 계속해서 당신을 괴롭힐 것이며 상태에 대한 추정은 당신이 고려하지 못했던 구문(syntax)에 의해 거의 항상 틀릴 것입니다.
아래의 코드를 봅시다:
```js
function square(n) {
return n * n;
}
```
`n`의 이름을 `x`로 바꾸는 빠르고 간단한 방문자를 작성해봅시다.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
위 코드는 아마 잘 동작 할테지만, 아래와 같은 경우 제대로 동작하지 않습니다:
```js
function square(n) {
return n * n;
}
n;
```
더 좋은 방법은 재귀(recursion) 를 쓰는 것입니다. Christopher Nolan의 영화처럼 방문자안에 방문자를 넣어봅시다.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
물론, 이건 부자연스러운 예제이긴 하지만 방문자에서 전역 상태(global state) 를 어떻게 제거할 수 있는지 보여줍니다.
### 범위(Scopes)
다음으로 [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)) 개념을 소개합니다. 자바스크립트는 [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping)을 가지고 있는데, 이것은 트리 구조이며 블록은 새로운 scope을 생성합니다.
```js
// 전역 스코프
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
자바스크립트에서는 참조(reference)를 선언할 경우, 그게 변수든, 함수든, 클래스든, 파라미터든, import든, label이든 뭐든지 간에 현재 scope에 속하게 됩니다.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
하위 scope 안의 코드에서는 상위 scope의 참조를 사용 할 수 있습니다.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
또한 하위 scope에서 상위 scope의 참조를 바꾸지 않고, 같은 이름의 참조를 생성할 수 있습니다.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
코드가 변환(transform) 이 될 때, 이 scope를 고려해야 합니다. 코드의 다른 부분이 수정되는 동안 기존 코드가 깨지지 않도록 해야 합니다.
우리는 새로운 변수들을 추가하고, 그 변수들이 기존의 것들과 충돌하지 않도록 할 수도 있습니다. 아니면 단순히 변수가 어디에서 참조되는지 찾을 수도 있습니다. 우리는 주어진 scope에서 이 참조들이 추적될 수 있도록 만들려고 합니다.
Scope는 다음과 같이 나타낼 수 있습니다:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
새로운 scope 생성 시 그것의 경로(path)와 부모(parent) scope를 전달함으로써 그렇게 할 수 있습니다. 그런 다음 그것은 탐색 과정 중 해당 scope의 모든 참조("bindings")들을 수집합니다.
이것이 끝나면, scope 안에서 사용할 수 있는 각종 메소드들이 있게 됩니다. 이것에 대해선 나중에 알아봅시다.
#### 바인딩(Bindings)
특정 scope에 속하는 모든 참조들; 이 관계를 **바인딩**(binding) 이라고 합니다.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // 바인딩에 대한 참조입니다
function scopeTwo() {
ref; // 낮은 스코프의 바인딩에 대한 참조입니다
}
}
```
하나의 binding은 이렇게 생겼습니다:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
이 정보들로 binding의 모든 참조들을 찾을 수 있고, 어떤 binding 타입(파라미터인지 선언인지 등등)인지 알 수 있고, 어떤 scope에 속하는지 찾거나, identifier의 사본을 얻을 수 있습니다. 또한 그것이 상수인지 알 수 있고, 만일 아니라면 어떤 path들이 그것을 변경했는지도 알 수 있습니다.
상수인지 아닌지는 다양한 이유로 유용한 정보인데, 가장 큰 이유는 minification 때문입니다.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel 은 사실 모듈들의 집합입니다. 이 장에서는 중요한 모듈들이 살펴보고, 그것들이 무엇을 하고, 어떻게 그것들을 사용할 것인지 설명합니다.
> 주의: 이 장은 곧 준비될 예정인 상세 API 문서를 대체하지는 않습니다.
## [`babylon`](https://github.com/babel/babylon)
Babylon 은 Bable의 분석기(parser) 입니다. Acorn 을 복제(fork) 하여 시작되었고, 빠르고 사용하기 간단하며, 비 표준 (그뿐만 아니라 미래의 표준) 기능을 위해 플러그인 기반의 구조를 가지고 있습니다.
먼저, 설치해봅시다.
```sh
$ npm install --save babylon
```
간단하게 코드의 문자열을 파싱 해보며 시작해봅시다.
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
`parse()`호출 시 아래와 같이 option들을 넘길 수도 있습니다:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` `"module"` 또는 `"script"` 가 될 수 있고 babylon이 어떤 모드로 파싱 하는지 정해줍니다. `"module"` 은 strict mode로 파싱하고 모듈 선언을 허용하지만, `"script"` 는 그렇지 않습니다.
> **주의:** `sourceType` 은 기본으로 `"script"` 이며 여기서`import`나 `export` 를 발견하면 에러를 발생할 것입니다. 이 에러를 해결하려면 `sourceType: "module"` 을 넘겨주세요.
Babylon은 플러그인 기반 설계로 만들어져서, 내부 플러그인들(`plugins`)을 작동 시킬 수 있는 옵션이 있습니다. 아마 나중에는 그렇게 되겠지만, Babylon은 아직 이 API를 외부 플러그인들에게 오픈하지 않았다는 것을 알아두세요.
플러그인들의 모든 목록은 [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins) 를 참고하세요..
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
Babel Traverse 모듈은 트리의 모든 상태를 관리하고, 노드들의 교체, 삭제, 추가하는 일을 담당합니다.
아래 명령어를 실행하여 설치합니다:
```sh
$ npm install --save babel-traverse
```
노드들을 탐색하고 업데이트하기 위해 Babylon과 함께 사용할 수 있습니다:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types 는 AST 노드들을 위한 Lodash 스타일의 유틸리티 라이브러리입니다. 이 라이브러리는 AST 노드들을 만들고(building), 검사하고(validating), 변경하기(converting) 위한 메소드들을 포함합니다. 검증된 유틸리티 메소드들을 사용하여 AST 로직을 정리할 때 매우 유용합니다.
아래 명령어를 실행하여 설치합니다:
```sh
$ npm install --save babel-types
```
그다음 이렇게 사용할 수 있습니다:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### 정의(Definitions)
Babel Types 는 모든 노드 type에 대한 정의들을 가지고 있는데, 어떤 속성이 어디에 속하는지, 어떤 값들이 유효한지, 이 노드를 어떻게 만드는지, 노드를 어떻게 탐색해야 하는지, 그리고 노드가 어떤 별명(aliases)을 가지는지에 대한 정보를 가집니다.
하나의 노드 type 정의는 이렇게 생겼습니다:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### 빌더(Builders)
위의 정의에서 `BinaryExpression` 이 `builder`를 위한 필드를 가지고 있다는 것을 알아차렸을 겁니다.
```js
builder: ["operator", "left", "right"]
```
이는 각 노드 타입이 빌더 메소드를 받기 때문인데, 다음과 같이 사용됩니다:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
이 코드는 아래와 같은 AST 를 생성합니다.
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
이것으로 소스 코드로 찍어보면 다음과 같습니다:
```js
a * b
```
빌더들은 또한 그들이 생성하는 노드들의 유효성을 검사하여 만약 부적절하다면 상세 에러를 던질 것입니다. 이것은 다음 메소드 설명에 이어집니다.
### 검증자(Validators)
`BinaryExpression` 정의는 노드의 `fields` 정보와 이것을 어떻게 검증(validate) 하는지에 대한 정보도 포함합니다.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
이것은 두 종류의 검증 메소드들을 만들기 위해 사용됩니다. 첫번째는 `isX`입니다..
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
이것은 노드가 binary expression 인지 보장하는 것뿐만 아니라 노드가 특정 속성들과 값들을 가지는지 확인하도록 두 번째 파라미터를 보낼 수 있습니다.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
*ehem*이라는 `true` 나 `false` 를 리턴하는 대신 에러를 던지는 assertive 형식의 메소드도 있습니다.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### 변환기(Converters)
> [작업중]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator는 Babel의 코드 생성기입니다. AST를 받아 소스 코드와 소스맵으로 바꿔줍니다.
아래 명령어를 실행하여 설치합니다:
```sh
$ npm install --save babel-generator
```
그 다음 이렇게 사용합니다
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
`generator()` 에게 옵션들(options) 을 넘겨줄 수도 있습니다.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template 은 작지만 굉장히 유용한 모듈입니다. 직접 거대한 AST 를 만드는 대신 데이터가 들어갈 곳을 표시자(placeholders) 로 나타내며 코드를 작성할 수 있게 해줍니다. 컴퓨터 과학 분야에선, 이런 기술을 quasiquotes라고 부릅니다.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# 첫 Babel 플러그인 작성하기
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# 변환 작업
## 방문하기(Visiting)
### 서브-노드의 경로 얻기
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### 노드가 어떤 형식인지 확인하기
노드의 형식이 무엇인지 확인하고 싶을 때 가장 권장되는 방법은 다음과 같습니다:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
또한 해당 노드의 속성에 대해 얕은 검사 (shallow check) 를 할 수도 있습니다.
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
이 코드는 아래의 코드와 동일한 기능을 합니다:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### 경로가 어떤 형식인지 확인하기
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### 식별자가 참조되었는지 확인하기
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### 특정한 부모 경로 찾기
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### 형제 경로 얻기
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### 탐색 중지하기
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## 조작(Manipulation)
### 노드 교체하기
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### 노드 하나를 여러 개의 노드로 교체하기
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### 노드 하나를 소스 문자열로 교체하기
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### 형제 노드를 삽입하기
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### 컨테이너에 삽입하기
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### 노드 삭제하기
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### 부모 교체하기
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### 부모 삭제하기
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## 범위(Scope)
### 지역 변수가 바인딩되었는지 확인하기
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### UID 생성하기
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### 부모 스코프에 변수 선언 집어넣기
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### 바인딩과 참조의 이름 바꾸기
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# 플러그인 옵션
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## 플러그인의 전처리와 후처리
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## 플러그인에서 구문 활성화하기
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## 구문 오류 throw 하기
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# 노드 만들기(Building Nodes)
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# 모범 사례
## 헬퍼 빌더와 체커 만들기
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## 가능한 AST 탐색을 피하라
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### 가능하다면 방문자들을 병합(merge) 하라
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### 수동 조회할 땐 탐색하지 마라
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## 중첩된 방문자들 최적화하기
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## 중첩된 구조(nested structures) 인지하기
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## 단위 테스트
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot 테스트
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST 테스트
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec 테스트
여기선 코드를 변환시켜보고, 올바르게 동작하는지 확인해보겠습니다. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***향후 업데이트를 받아보려면 Twitter에 있는 [@thejameskyle](https://twitter.com/thejameskyle) 와 [@babeljs](https://twitter.com/babeljs) 를 팔로우하세요.***
================================================
FILE: translations/ko/user-handbook.md
================================================
# Babel User Handbook
이 문서는 모든 [Babel](https://babeljs.io)과 관련된 도구를 사용하는 방법에 대해 설명합니다.
[](http://creativecommons.org/licenses/by/4.0/)
이 핸드북은 다른 언어로도 볼 수 있습니다. 전체 목록은 [README](/README.md)를 참고하세요.
# 목차
* [소개](#toc-introduction)
* [Babel 설정하기](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [프로젝트 내에서 Babel CLI 실행하기](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Babel 컨픽 작성하기](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Babel로 생성된 코드 실행하기](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Babel 컨픽 작성하기 (고급)](#toc-configuring-babel-advanced)
* [수동으로 플러그인 지정하기](#toc-manually-specifying-plugins)
* [플러그인 옵션](#toc-plugin-options)
* [환경에 따른 Babel 커스터마이징](#toc-customizing-babel-based-on-environment)
* [나만의 프리셋 만들기](#toc-making-your-own-preset)
* [Babel과 기타 도구들](#toc-babel-and-other-tools)
* [정적 분석 도구](#toc-static-analysis-tools)
* [코드 검사기 (Linter)](#toc-linting)
* [코드 스타일](#toc-code-style)
* [문서](#toc-documentation)
* [프레임워크](#toc-frameworks)
* [React](#toc-react)
* [텍스트 편집기와 IDE](#toc-text-editors-and-ides)
* [Babel 지원](#toc-babel-support)
* [Babel 포럼](#toc-babel-forum)
* [Babel 채팅](#toc-babel-chat)
* [Babel 이슈](#toc-babel-issues)
* [멋진 Babel 버그 보고 생성](#toc-creating-an-awesome-babel-bug-report)
# 소개
Babel은 JavaScript를 위한 일반적인 다중 목적 컴파일러입니다. Babel은 다음 세대의 JavaScript를 사용할 수 있도록 해주고 (만들 수 있도록), 그 뿐만 아니라 다음 세대의 JavaScript 유용한 유틸리티를 제공합니다.
JavaScript는 언어로써 새로운 스팩과 제안과 새로운 기능들이 언제나 나오며 끊임없이 발전하고 있습니다. Babel을 사용하면 언어적 기능이 본격적으로 모든 곳에서 사용되기 이전에 모든 기능을 미리 사용할 수 있습니다.
Babel은 최신 표준으로 작성된 JavaScript 코드를 현재 어디서나 동작하는 코드로 컴파일합니다. 이 과정은 source-to-source 컴파일이라고도 부르고 트랜스파일이라고도 부릅니다.
예를 들어, Babel은 ES2015의 새로운 화살표 함수 문법을 변환할 수 있습니다:
```js
const square = n => n * n;
```
이를 다음과 같이 변환합니다:
```js
const square = function square(n) {
return n * n;
};
```
하지만 Babel은 React의 JSX 문법이나 정적 타입 검사를 위한 Flow 문법 지원 같은 문법 확장도 지원하므로 위에서 본 것보다 더 많은 일을 할 수 있습니다.
더 나아가 Babel에서 모든 것은 단순한 플러그인이고 원한다면 언제든지 Babel의 모든 기능을 사용해서 자신만의 플러그인을 만들 수 있습니다.
*여기서 더 나아가* Babel은 차세대 JavaScript 도구를 만드는데 사용할 수 있는 다수의 핵심 모듈로 모듈화되어 있습니다.
많은 사람들 하듯이 갑자기 등장한 Babel의 생태계는 아주 크고 매우 다양합니다. 이 핸드북에서 Babel의 내장 도구들이 동작하는 원리와 커뮤니티에서 만든 유용한 내용을 다룰 것입니다.
> ***향후 업데이트에 대한 내용은 Twitter의 [@thejameskyle](https://twitter.com/thejameskyle)를 팔로우하세요.***
* * *
# Babel 설정하기
JavaScript 커뮤니티가 다양한 빌드도구, 프레임워크, 플랫폼 등을 가지므로 Babel은 주요 도구를 공식적으로 통합하고 있습니다. Gulp에서 Browserify 까지, Ember에서 Meteor까지 공식적인 통합이 있을 것 같은 모든 것을 설정해서 사용할 수 있습니다.
이 핸드북의 목적에 따라 Babel을 구성하는 내장된 방법을 설명하고 있지만 다른 통합에 대해서는 인터렉티브한 [설정 페이지](http://babeljs.io/docs/setup)를 사용할 수도 있습니다.
> **Note:** 이 가이드에서는 `node`와 `npm`같은 커맨드라인 도구를 사용합니다. 더 진행하기 전에 이러한 도구에 익숙해 져야 합니다.
## `babel-cli`
Babel의 CLI는 커맨드라인에서 Babel로 파일을 컴파일하는 간단한 방법입니다.
일단 기본을 배우기 위해 전역으로 설치해 보겠습니다.
```sh
$ npm install --global babel-cli
```
첫 파일을 다음과 같이 컴파일할 수 있습니다.
```sh
$ babel my-file.js
```
터미널에 컴파일된 결과가 바로 나타납니다. 컴파일된 결과를 파일에 쓰려면 `--out-file`나 `-o`을 지정해야 합니다..
```sh
$ babel example.js --out-file compiled.js
# 또는
$ babel example.js -o compiled.js
```
디렉터리 전체를 새로운 디렉터리로 컴파일하고 싶다면 `--out-dir`나 `-d`를 사용합니다..
```sh
$ babel src --out-dir lib
# 또는
$ babel src -d lib
```
### 프로젝트 내에서 Babel CLI 실행하기
자신의 머신에 Babel CLI를 전역으로 *설치할 수도 있지만* 프로젝트 **내부에** Babel을 설치하는 것이 훨씬 더 좋습니다.
내부에 설치하는 이유가 두 가지 있습니다.
1. 같은 머신에서 다른 프로젝트는 다른 버전의 Babel을 사용할 수 있으므로 한번에 하나씩 업데이트 할 수 있습니다.
2. 이는 작업하는 환경에 암묵적인 의존성을 갖지 않는다는 의미입니다. 프로젝트를 이식하고 구성하기 쉽게 만드세요.
다음 명령어로 Babel CLI를 프로젝트 내부에 설치할 수 있습니다.
```sh
$ npm install --save-dev babel-cli
```
> **참고:** 전역에서 Babel을 실행하는 것은 좋지 않은 생각이므로 다음 명령으로 전역에 설치된 Babel을 지울 수 있습니다:
>
> ```sh
$ npm uninstall --global babel-cli
```
설치가 끝나면, `package.json`이 다음과 같이 표시되어야 합니다:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
이제 CLI에서 Babel을 직접적으로 사용하는 대신, **npm scripts**를 추가하여 로컬 버전의 Babel을 사용하도록 명령어를 추가합니다.
간단히 `package.json`에 `"scripts"` 필드를 추가한 후 `build` 명령어와 같이 Babel 명령어를 추가하세요.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
이제 터미널에서 다음을 실행할 수 있습니다:
```js
npm run build
```
이 명령어는 이전과 같이 Babel을 실행하지만, 로컬에 설치된 Babel을 사용합니다.
## `babel-register`
Babel을 실행하는 또 다른 방법의 하나는 `babel-register`를 사용하는 것입니다. 이 방법을 이용하면 파일을 require하는 것만으로도 Babel을 실행할 수 있어 더 간단하게 설정에 통합할 수 있을 것입니다.
이는 프로덕션에서 사용할 목적이라는 의미는 아닙니다. 이 방법으로 컴파일되는 코드를 배포하는 것은 좋지 않은 방법입니다. 배포하기 전에 미리 컴파일하는 것이 훨씬 나은 방법입니다. 하지만 빌드 스크립트나 로컬에서 실행하는 다른 작업에서는 아주 잘 동작합니다.
먼저 프로젝트에 `index.js`을 생성해 보겠습니다.
```js
console.log("Hello world!");
```
이 파일을 `node index.js`로 실행한다면 Babel로 컴파일되지 않을 것이므로 대신 `babel-register`를 설정해 보겠습니다..
먼저 `babel-register`를 설치하세요.
```sh
$ npm install --save-dev babel-register
```
그다음 프로젝트에 `register.js` 파일을 생성하고 다음 코드를 작성하세요.
```js
require("babel-register");
require("./index.js");
```
이 코드는 Node의 모듈 시스템에 Babel을 *등록하고* `require`하는 모든 파일을 컴파일합니다.
이제 `node index.js`를 실행하는 대신 `register.js`를 사용할 수 있습니다.
```sh
$ node register.js
```
> **Note:** Babel이 코드를 컴파일하기 전에 node가 파일을 실행하므로 컴파일하려는 파일에서 Babel을 등록할 수는 없습니다.
>
> ```js
require("babel-register");
// 컴파일되지 않음:
console.log("Hello world!");
```
## `babel-node`
`node` CLI로 코드를 실행한다면 `node` CLI의 대체물인 `babel-node` CLI를 사용하는 것이 Babel을 통합하는 가장 쉬운 방법입니다.
이는 프로덕션에서 사용할 목적이라는 의미는 아닙니다. 이 방법으로 컴파일되는 코드를 배포하는 것은 좋지 않은 방법입니다. 배포하기 전에 미리 컴파일하는 것이 훨씬 나은 방법입니다. 하지만 빌드 스크립트나 로컬에서 실행하는 다른 작업에서는 아주 잘 동작합니다.
일단 `babel-cli`가 설치되었는지 확인합니다.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** 왜 이것을 로컬에 설치하는지 궁금하다면, [프로젝트에서 Babel CLI 실행하기](#toc-running-babel-cli-from-within-a-project) 부분을 읽어 보세요.
그 다음 `node`를 사용하는 대신 `babel-node`를 사용하세요..
npm `scripts`를 사용한다면 다음과 같이 설정할 수 있습니다.
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
아니면 `babel-node`의 경로를 적어주어야 합니다.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: [`npm-run`](https://www.npmjs.com/package/npm-run)을 사용할 수도 있습니다..
## `babel-core`
만약 어떠한 이유로 Babel을 프로그래밍 방식으로 사용해야 하는 경우, `babel-core` 패키지 자체를 사용할 수도 있습니다.
먼저 `babel-core`를 설치합니다.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
만약 JavaScript 문자열이 있는 경우 `babel.transform`를 통해 바로 컴파일 할 수 있습니다..
```js
babel.transform("code();", options);
// => { code, map, ast }
```
만약 파일로 작업하고 있다면 비동기 api와 함께 사용할 수 있습니다:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
또는 동기 api를 사용할 수도 있습니다:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
만약 어떠한 이유로 이미 Babel AST를 가지고 있다면 AST를 통해 직접 변환할 수 있습니다.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Babel 설정 파일 작성하기
아마 Babel을 실행했을 때 JavaScript 파일들을 다른 경로로 복사하는 것 말고는 아무 일도 하지 않는다는 것을 눈치챘을 겁니다.
이는 아직 Babel에게 할 일을 아무것도 주지 않았기 때문입니다.
> Babel이 범용 컴파일러가 된 이후부터 무수히 많은 용도로 쓰이게 되었으나 기본적으론 아무 일도 하지 않습니다. 따라서 명시적으로 Babel에게 할 일을 알려주어야 합니다.
**플러그인** 또는 **프리셋** (플러그인 모음)을 설치함으로써 Babel에게 지시를 내릴 수 있습니다.
## `.babelrc`
Babel에게 무엇을 해야 하는지 지시하기 전에, 파일을 하나 만들어야 합니다. 작업하기 전에 가장 먼저 할 것은 프로젝트의 루트에 `.babelrc` 파일을 만드는 것입니다. 다음과 같이 작성합니다:
```js
{
"presets": [],
"plugins": []
}
```
이 파일은 Babel이 어떤 작업을 해야 할 지 구성합니다.
> **참고:** 또한 `.babelrc`에서 Babel에 옵션을 전달할 수도 있으며 가장 좋은 방법입니다.
## `babel-preset-es2015`
이제 Babel에게 ES2015 (ES6으로 알려진 새로운 버전의 JavaScript 표준)를 ES5 (현대 대부분의 환경이 실행할 수 있는 JavaScript 버전)로 컴파일하도록 지시해봅시다.
위 커맨드는 "es2015" Babel 프리셋을 설치할 것입니다:
```sh
$ npm install --save-dev babel-preset-es2015
```
다음은 `.babelrc`가 프리셋을 포함하도록 수정합니다.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
React를 설정하는 것은 매우 쉽습니다. 그저 프리셋을 설치하고:
```sh
$ npm install --save-dev babel-preset-react
```
`.babelrc` 파일에 프리셋을 추가합니다:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
자바스크립트는 표준으로 만들기 위한 몇 가지 제안 사항을 가지고 있고 TC39의 진행 상황에 따라 표준이 제정됩니다. (ECMAScript 표준 기술 위원회)
이 과정은 5 스테이지 (0-4) 로 진행됩니다. 제안은 어느 정도의 호응과 함께 표준이 되어야 한다고 판단되면 스테이지에 따라 표준화 작업을 진행합니다. 최종 표준으로 받아들여질 제안은 stage 4에 위치됩니다.
Babel에선 다음 4가지 프리셋으로 번들되어 있습니다:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> stage-4 프리셋은 단순히 `es2015`이며 따로 프리셋이 존재하진 않습니다.
각 프리셋들은 이후의 프리셋을 자동으로 포함합니다. 즉, `babel-preset-stage-1`은 `babel-preset-stage-2`를 포함하며 다시 이 프리셋은 `babel-preset-stage-3`를 포함합니다.
관심이 있다면 단순히 다음과 같이 원하는 스테이지를 설치합니다:
```sh
$ npm install --save-dev babel-preset-stage-2
```
그런 다음 `.babelrc` 설정에 추가할 수 있습니다.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Babel로 생성된 코드 실행하기
그래서 일단 Babel로 코드를 컴파일 했습니다, 하지만 아직 끝나지 않았습니다.
## `babel-polyfill`
거의 모든 미래적인 JavaScript 문법은 Babel로 컴파일할 수 있습니다. 하지만 API는 그렇지 않습니다.
예를 들어, 다음 코드는 화살표 함수를 포함하고 있고 컴파일되어야 합니다:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
위 코드는 다음과 같이 변환됩니다:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
그러나, 모든 JavaScript 환경에 `Array.from`가 있지 않기 때문에 여전히 어디서나 작동하진 않을 것입니다.
Uncaught TypeError: Array.from is not a function
이러한 문제는 [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill)을 사용함으로써 해결할 수 있습니다. 간단히 설명하자면, polyfill은 현재 런타임에 존재하지 않는 네이티브 API를 흉내내는 코드입니다. 이로써 `Array.from` 같은 API들을 환경의 지원에 상관없이 언제나 사용할 수 있습니다.
Babel은 우수한 [core-js](https://github.com/zloirock/core-js)를 polyfill로 사용하고, 생성기와 async 함수가 작동할 수 있도록 약간 변경한 [regenerator](https://github.com/facebook/regenerator) 런타임을 사용합니다.
Babel polyfill을 포함하려면, 먼저 npm으로 설치합니다:
```sh
$ npm install --save babel-polyfill
```
그리고 간단히 어떤 파일의 가장 상단에 다음 코드를 추가합니다:
```js
import "babel-polyfill";
```
## `babel-runtime`
ECMAScript 스팩의 상세 구현을 따르기 위함과 동시에 생성되는 코드를 깨끗하게 유지하기 위해, Babel은 "helper" 메서드를 사용할 것입니다.
이러한 헬퍼 코드는 상당히 길고 매 파일 위에 삽입되어야 하기 때문에, 필요하다면 단일 "runtime"으로 모든 헬퍼 코드를 옮길 수 있습니다.
먼저 `babel-plugin-transform-runtime`과 `babel-runtime`을 설치합니다:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
그리고 `.babelrc`를 수정하세요:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
이제 Babel은 다음 코드를:
```js
class Foo {
method() {}
}
```
다음과 같이 컴파일할 것입니다:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
필요할 때마다 매 파일에 `_classCallCheck`와 `_createClass` 헬퍼를 삽입하는 대신 위와 같이 런타임 헬퍼를 사용하는 것으로 변경됩니다.
* * *
# Babel 설정 파일 작성하기 (고급)
대부분의 사람들은 Babel과 빌트인 프리셋만을 사용합니다. 하지만 Babel은 더 세분화된 작업을 할 수 있습니다.
## 수동으로 플러그인 지정하기
Babel 프리셋은 미리 구성된 플러그인들의 집합입니다. 만약 플러그인을 특정하여 다른 작업을 하고 싶다면, 프리셋과 거의 정확히 같은 방식으로 작동합니다.
먼저 플러그인을 설치합니다:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
그리고 `.babelrc`의 `plugins` 필드에 추가합니다.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
이는 세분화된 트랜스폼을 통해 세밀한 작업을 할 수 있게 해줍니다.
전체 공식 플러그인 리스트는 [Babel Plugins 페이지](http://babeljs.io/docs/plugins/)에서 확인할 수 있습니다.
또한 이미 [커뮤니티에 의해 만들어진](https://www.npmjs.com/search?q=babel-plugin) 모든 플러그인을 사용할 수도 있습니다. 만약 자신만의 플러그인을 작성하는 방법을 배우고 싶다면 [Babel Plugin Handbook](plugin-handbook.md)을 읽으세요..
## 플러그인 옵션
또한 많은 플러그인들이 다르게 동작 하도록 구성할 수 있는 옵션을 가지고 있습니다. 예를 들면, 많은 트랜스폼이 일부 스펙을 지키지 않는 대신 생성되는 코드의 성능을 개선하고 단순하게 만드는 "loose" 모드를 가지고 있습니다.
플러그인에 옵션을 추가하려면, 간단히 설정을 다음과 같이 변경하면 됩니다:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> 앞으로 몇 주 내로 모든 플러그인 옵션의 상세 사항을 포함하도록 플러그인 문서를 업데이트할 예정입니다. [업데이트 소식을 얻으려면 팔로우](https://twitter.com/thejameskyle).
## 환경에 따른 Babel 커스터마이징
Babel 플러그인은 많은 다른 작업을 해결합니다. 대부분의 경우는 개발 툴이며 코드를 디버깅하거나 다른 툴과 통합할 수 있도록 도와줍니다. 또한 실제 프로덕션 코드를 위한 수 많은 최적화 플러그인도 많이 있습니다.
이러한 이유로 인해 Babel을 환경에 따라 구성을 변경하려 하는 것이 일반적입니다. `.babelrc`에서 쉽게 할 수 있습니다.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel은 현재 환경에 따라 `env` 내에 정의된 구성을 활성화 할 것입니다.
현재 환경은 `process.env.BABEL_ENV` 변수를 사용합니다. `BABEL_ENV`를 사용할 수 없다면, `NODE_ENV`를 대신 사용합니다. 만약 둘 다 사용할 수 없다면 기본으로 `"development"`가 지정됩니다..
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **참고:** `[COMMAND]`는 Babel을 실행하기 위한 명령어입니다. (ie. `babel`, `babel-node`, 또는 register 훅을 사용할 경우 그냥 `node`)
>
> **Tip:** 만약 명령어가 unix와 windows 플랫폼에서 모두 작동하도록 하고 싶다면 [`cross-env`](https://www.npmjs.com/package/cross-env)를 사용하세요..
## 나만의 프리셋 만들기
직접 플러그인과 플러그인 옵션, 환경에 따른 설정을 지정하는 경우 모든 설정 과정이 프로젝트마다 되풀이되는 것을 느낄 것입니다.
이러한 이유로, 우리는 커뮤니티가 용도에 맞는 프리셋을 직접 만드는 것을 권장합니다. 이는 실행하고 있는 [node 버전](https://github.com/leebenson/babel-preset-node5)에 대해 특정되거나 [전적](https://github.com/cloudflare/babel-preset-cf)으로 [회사](https://github.com/airbnb/babel-preset-airbnb)에 관련된 프리셋이 될 수 있습니다..
프리셋을 만드는 것은 간단합니다. 다음과 같은 `.babelrc`를 프리셋으로 만드려면:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
프리셋을 만들기 위해 해야 할 일은 단지 새 프로젝트를 만들고 `babel-preset-*`과 같은 명명법을 따르는 것 입니다. (네임 스페이스를 만들었다면 꼭 책임을 지시기 바랍니다) 그리고 두 개의 파일을 생성합니다.
먼저, 새 `package.json` 파일을 만든 후 프리셋에서 요구하는 `종속성`들을 설치합니다.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
그리고 `index.js` 파일을 생성한 후 `.babelrc`의 내용을 export하도록 만들고 플러그인/프리셋 문자열을 `require` 호출로 바꿉니다.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
그리고 간단히 npm에 배포하면 이 프리셋을 원하는 곳에 사용할 수 있습니다.
* * *
# Babel과 기타 도구들
Babel은 설치 방법이 아주 직관적입니다. 하지만 다른 툴과 함께 사용하는 경우라면 설정하는 방법을 탐색하는 것이 어려울 수 있습니다. 그러나, 우리는 다른 프로젝트와의 운용을 쉽게 만들기 위해 가능한 한 가깝게 만들고 있습니다.
## 정적 분석 도구
새로운 표준은 언어로 수 많은 새 문법을 가져오고 있으며 정적 분석 도구는 이를 그대로 활용합니다.
### 코드 검사기 (Linter)
코드 검사 도구 중 가장 인기있는 도구는 [ESLint](http://eslint.org)이며, 이에 따라 공식적으로 [`babel-eslint`](https://github.com/babel/babel-eslint)를 제공하고 있습니다.
먼저 `eslint`와 `babel-eslint`를 설치합니다.
```sh
$ npm install --save-dev eslint babel-eslint
```
그 다음 프로젝트에 새 `.eslintrc` 파일을 만들거나 이미 존재하는 설정에서 `파서`로 `babel-eslint`를 사용하도록 설정합니다.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
이제 `package.json`의 스크립트에 `lint` 작업을 추가합니다:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
그리고 작업을 실행하면 모든 설정이 완료됩니다.
```sh
$ npm run lint
```
더 자세한 사항은 [`babel-eslint`](https://github.com/babel/babel-eslint) 또는 [`eslint`](http://eslint.org) 문서를 참고하세요.
### 코드 스타일
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS는 코드 검사의 추가 단계에 있어서 코딩 스타일 자체를 점검하는 매우 인기 있는 코드 검사 도구입니다. Babel과 JSCS 프로젝트의 핵심 관리자 ([@hzoo](https://github.com/hzoo))는 공식적으로 JSCS와의 통합을 제공하고 있습니다.
더 나은 방법으로는, JSCS 자체의 `--esnext` 옵션을 통해 쉽게 Babel과 통합하는 방법도 있습니다:
$ jscs . --esnext
CLI에서 실행하거나 `.jscsrc` 파일에 `esnext` 옵션을 추가해도 됩니다.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
더 자세한 정보는 [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) 또는 [`jscs`](http://jscs.info) 문서를 참고하세요.
### 문서
Babel, ES2015, Flow를 사용함으로써 코드의 많은 부분을 추론할 수 있습니다. [documentation.js](http://documentation.js.org)를 사용하면 자세한 API 문서를 쉽게 생성할 수 있습니다.
Documentation.js는 Flow 표기법이 코드 내에서 타입을 정의함과 동시에 최신의 문법을 지원하기 위해 내부적으로 Babel을 사용합니다.
## 프레임워크
모든 주요 JavaScript 프레임워크의 API들은 이제 언어의 미래를 향해 초점이 맞춰졌습니다. 이 때문에 툴링에 많은 일이 진행되고 있습니다.
프레임워크들은 Babel을 사용하는 것뿐만 아니라 확장하여 사용자 경험을 더 강화할 기회가 있습니다.
### React
React는 극적으로 ES2015의 클래스에 맞춰 API를 변경했습니다. ([업데이트된 API는 이곳에서 확인](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)) 더욱이, React는 JSX 구문의 컴파일을 Babel에 의존하며 Babel을 사용함으로써 그들의 커스텀 툴의 사용을 반대합니다. `babel-preset-react` 패키지와 함께 [위의 절차](#babel-preset-react)에 따라 시작할 수 있습니다..
React 커뮤니티는 Babel과 함께 성장합니다. [커뮤니티에 의해 만들어진](https://www.npmjs.com/search?q=babel-plugin+react) 트랜스폼의 개수가 상당해졌습니다..
가장 주목할 것은 몇 가지의 [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms)과 함께 *hot module reloading* 그리고 디버깅 유틸리티를 활성화 시킬 수 있는 [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform)입니다.
## 텍스트 편집기와 IDE
Babel과 함께 ES2015와 JSX와 Flow 문법을 사용하는 것은 매우 유용할 수 있습니다. 하지만 텍스트 편집기가 지원하지 않는 경우 정말 좋지 않은 경험이 될 수 있습니다. 이러한 이유로 사용하는 IDE와 함께 Babel 플러그인을 사용하는 방법을 알아보고 싶을 것입니다. 다음을 참고하세요:
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel 지원
Babel은 아주 크고 빠르게 성장하는 커뮤니티를 가지고 있으며, 성장하면서 사람들이 확실히 원하는 리소스를 성공적으로 얻을 수 있도록 도움을 주기 위해 노력하고 있습니다. 그래서 우리는 지원을 위해 서로 다른 채널을 제공하고 있습니다.
참고로 모든 커뮤니티는 [행동 강령](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md)을 적용받는다는 것을 기억하세요. 만약 행동 강령을 어길 경우, 조치가 취해질 것입니다. 따라서 위 문서를 읽고 다른 이들과 상호작용할 때 유의하시기 바랍니다.
우리는 사람들이 커뮤니티에 머무르며 다른 사람에게 지원하는, 자체적인 지원을 하는 커뮤니티를 목표로 하고 있습니다. 만약 다른 이들이 질문하고 있고 스스로가 이 질문에 대한 답을 안다면, 약간의 시간을 들여 그들을 도와주세요. 최대한 친절하고 이해하려 노력해주세요.
## Babel 포럼
[Discourse](http://www.discourse.org)는 호스팅 버전의 포럼 소프트웨어를 무료로 우리에게 제공하고 있습니다. (그리고 우리는 그들을 매우 사랑합니다!) 만약 포럼에 볼일이 있다면 [discuss.babeljs.io](https://discuss.babeljs.io)에 들러주세요..
## Babel 채팅
모두가 [Slack](https://slack.com)을 사랑합니다. 만약 즉각적인 커뮤니티 지원을 알아보고 있다면 [slack.babeljs.io](https://slack.babeljs.io)에 참여하여 도움을 요청하세요.
## Babel 이슈
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
만약 새로운 이슈를 만들고 싶다면:
* [먼저 해당 이슈가 이미 있는지 검색하세요](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### 멋진 Babel 버그 보고 생성
가끔 Babel의 이슈는 원격으로 디버깅하기 상당히 어려울 때가 있습니다, 따라서 우리는 가능한 한 모든 도움을 받아야 합니다. 약간의 시간을 들여 정말 멋진 버그 리포트를 작성하여 개시한다면 해당 문제가 빠르게 해결되는 데 큰 도움이 됩니다.
첫째, 문제를 고립시킵니다. 설치 과정이 문제의 모든 부분에서 영향을 미치고 있을 가능성이 큽니다. 만약 문제가 코드내에 있다면, 같은 문제가 발생하도록 최대한 코드를 줄여주세요.
> [작업중]
* * *
> ***향후 업데이트에 대한 내용은 Twitter의 [@thejameskyle](https://twitter.com/thejameskyle)를 팔로우하세요.***
================================================
FILE: translations/nl/README.md
================================================
# Babel handboek
Dit handboek is onderverdeeld in twee delen:
* [Gebruikershandleiding](user-handbook.md) - hoe Babel in te stellen en te configureren.
* [Plugin handboek](plugin-handbook.md) - hoe plugins voor Babel te maken.
> Voor toekomstige updates, volg [@thejameskyle](https://twitter.com/thejameskyle) op Twitter.
Als je een niet-Engelse vertaling van dit handboek leest, kun je Engelse delen tegenkomen die nog niet vertaald zijn. Als je wil bijdragen aan één van de vertalingen van dit document, moet je dat doen via Crowdin. Lees alsjeblieft de [bijdragerichtlijnen](/CONTRIBUTING.md) voor meer informatie. Nu en dan zal je Engelse programmeertermen tegenkomen. In dat geval is het logischer om de originele term te gebruiken om consistent te blijven. In de meeste gevallen zal je een letterlijke vertaling vinden, gevolgd door de Engelse term tussen haakjes `()`. bijvoorbeeld: Abstracte syntaxbomen (AST's).
================================================
FILE: translations/nl/plugin-handbook.md
================================================
# Babel Plugin Handleiding
Dit document beschrijft hoe [Babel](https://babeljs.io) [plugins](https://babeljs.io/docs/advanced/plugins/) te maken.
[](http://creativecommons.org/licenses/by/4.0/)
Deze gebruikershandleiding is beschikbaar in andere talen, zie de [README](/README.md) voor een volledige lijst.
# Inhoudsopgave
* [Introductie](#toc-introduction)
* [Basis](#toc-basics)
* [Abstracte syntaxisbomen (AST)](#toc-asts)
* [Stages of Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Lexical Analysis](#toc-lexical-analysis)
* [Syntactic Analysis](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Writing your first Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introductie
Babel is a generic multi-purpose compiler for JavaScript. More than that it is a collection of modules that can be used for many different forms of static analysis.
> Static analysis is the process of analyzing code without executing it. (Analysis of code while executing it is known as dynamic analysis). The purpose of static analysis varies greatly. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
You can use Babel to build many different types of tools that can help you be more productive and write better programs.
> ***Voor toekomstige updates, volg [@thejameskyle](https://twitter.com/thejameskyle) op Twitter.***
* * *
# Basis
Babel is a JavaScript compiler, specifically a source-to-source compiler, often called a "transpiler". This means that you give Babel some JavaScript code, Babel modifies the code, and generates the new code back out.
## Abstracte syntaxisbomen (AST)
Elk van deze stappen heeft te maken met[Abstracte Syntaxis-bomen](https://en.wikipedia.org/wiki/Abstract_syntax_tree), in het Engels afgekort als AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Je kan [AST Explorer](http://astexplorer.net/) bekijken om wat meer inzicht in AST-bomen. [Hier](http://astexplorer.net/#/Z1exs6BWMq) kan je een voorbeeld met deze code bekijken.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Of als een JavaScript Object zoals hier:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Je zal merken dat elk niveau van de AST een gelijkaardige structuur heeft:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Noot: enkele properties zijn weggelaten voor de eenvoudigheid.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Stages of Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Parse
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntactic Analysis
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transform
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Writing your first Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/nl/user-handbook.md
================================================
# Babel Gebruikershandleiding
Dit document bevat alles wat je ooit zou willen weten over het gebruik van [Babel](https://babeljs.io) en gerelateerde tooling.
[](http://creativecommons.org/licenses/by/4.0/)
Deze gebruikershandleiding is beschikbaar in andere talen, zie de [README](/README.md) voor een volledige lijst.
# Inhoudsopgave
* [Introductie](#toc-introduction)
* [Babel instellen](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Babel configureren](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Babel-gegenereerde code uitvoeren](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Babel configureren (Geavanceerd)](#toc-configuring-babel-advanced)
* [Plugins handmatig specificeren](#toc-manually-specifying-plugins)
* [Plugin opties](#toc-plugin-options)
* [Babel aanpassen gebaseerd op je omgeving](#toc-customizing-babel-based-on-environment)
* [Je eigen preset maken](#toc-making-your-own-preset)
* [Babel en andere tools](#toc-babel-and-other-tools)
* [Tools voor statische analyse](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Introductie
Babel is een generieke multifunctionele compiler voor JavaScript. Met behulp van Babel kan u de volgende generatie JavaScript gebruiken (en creëren), alsmede de volgende generatie van JavaScript hulpmiddelen.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Met behulp van Babel, kan je veel van deze functies reeds gebruiken, jaren voordat ze overal beschikbaar zijn.
Babel doet dit door de JavaScript-code geschreven volgens de laatste standaarden, te compileren naar een versie die heden overal zal werken. Dit proces staat bekend als bron-naar-bron compileren, ook bekend als transpileren.
Bijvoorbeeld, Babel kan de nieuwe ES2015 pijl functie syntaxis transformeren van dit:
```js
const vierkantswortel = n = > n * n;
```
In het volgende:
```js
const vierkantswortel = function vierkantswortel(n) {
return n * n;
};
```
Babel kan echter veel meer dan dit aangezien Babel ondersteuning biedt voor syntaxis extensies, zoals de JSX syntaxis voor React en Flow syntaxis voor controle van 'static type checking'.
Verder dan dat, is alles in Babel gewoon een plugin en iedereen kan zelfgemaakte plugins maken die gebruik maken van de volledige kracht van Babel, om te doen wat ze maar wensen.
*Nog meer* dan dat, is Babel onderverdeeld in een aantal basismodules die iedereen gebruiken kan om te bouwen aan de volgende generatie van JavaScript hulpgereedschap.
Veel mensen doen dat ook, het ecosysteem dat is ontstaan rond Babel is massaal en zeer divers. In dit handboek zal ik het zowel hebben over hoe de ingebouwde Babel tools werken, alsook sommige nuttige dingen uit de gemeenschap.
> ***Voor toekomstige updates, volg [@thejameskyle](https://twitter.com/thejameskyle) op Twitter.***
* * *
# Babel instellen
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Babel configureren
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Babel-gegenereerde code uitvoeren
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Babel configureren (Geavanceerd)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Plugins handmatig specificeren
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin opties
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Babel aanpassen gebaseerd op je omgeving
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Je eigen preset maken
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel en andere tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Tools voor statische analyse
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***Voor toekomstige updates, volg [@thejameskyle](https://twitter.com/thejameskyle) op Twitter.***
================================================
FILE: translations/no/README.md
================================================
# Babel Handbook
This handbook is divided into two parts:
* [User Handbook](user-handbook.md) - How to setup/configure Babel and more.
* [Plugin Handbook](plugin-handbook.md) - How to create plugins for Babel.
> For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.
If you are reading a non-english translation of this handbook you may still find english sections that have not yet been translated. If you would like to contribute to one of the translations you must do so through Crowdin. Please read the [contributing guidelines](/CONTRIBUTING.md) for more information. You will find a number of english words that are programming concepts. If these were translated to other languages there would be a lack of consistency and fluency when reading about them. In many cases you will find the literal translation followed by the english term in parenthesis `()`. For example: Abstract Syntax Trees (ASTs).
================================================
FILE: translations/no/plugin-handbook.md
================================================
# Babel Plugin Handbook
This document covers how to create [Babel](https://babeljs.io) [plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduksjon](#toc-introduction)
* [Grunnleggende](#toc-basics)
* [AST(Abstrakte-Syntaks-Tre)](#toc-asts)
* [Ulike Stadier av Babel](#toc-stages-of-babel)
* [Analyse](#toc-parse)
* [Leksikal Analyse](#toc-lexical-analysis)
* [Syntaks Analyse](#toc-syntactic-analysis)
* [Transformasjon](#toc-transform)
* [Generering](#toc-generate)
* [Traversering](#toc-traversal)
* [Gjester](#toc-visitors)
* [Baner](#toc-paths)
* [Besøksbaner til gjester](#toc-paths-in-visitors)
* [Tilstand](#toc-state)
* [Sikte](#toc-scopes)
* [Bindinger](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definisjoner](#toc-definitions)
* [Byggeklosser](#toc-builders)
* [Validatorer](#toc-validators)
* [Formaterere](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Å skrive din første Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Transformasjon operasjoner](#toc-transformation-operations)
* [Besøking](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulasjon](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Sikte](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin alternativer](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Å bygge noder](#toc-building-nodes)
* [Gode øvelser](#toc-best-practices)
* [Unngå å traversere AST-en så mye som mulig](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Kombinere gjester når det er mulig](#toc-merge-visitors-whenever-possible)
* [Ikke bruk traversering når det kan gjøres manuelt](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimalisere nestede gjester](#toc-optimizing-nested-visitors)
* [Vær oppmerksom på struktur og hvordan den er bygd opp](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introduksjon
Babel er en generisk kompilerer, som er brukbar på mange områder. Den er laget for JavaScript. Utenom det, er det en samling av moduler som kan brukes til mange former av statisk analyse.
> Statisk analyse er prosessen med å analysere koden uten å kjøre den. (Analyse av koden mens utfører, kalles dynamisk analyse). Formålet med statisk analyse varierer mye. Den kan brukes for linting, kompilering, utheving av kode, transformasjon av kode, optimalisering, minifikasjon og mye mer.
Du kan bruke Babel for å bygge mange forskjellige typer verktøy som kan hjelpe deg å bli mer produktiv å skrive bedre programmer.
> ***For fremtidige oppdateringer, følg [@thejameskyle](https://twitter.com/thejameskyle) på Twitter.***
* * *
# Grunnprinsipp
Babel er en JavaScript kompilerer, og er en blokk-til-kilde kompilerer, og er ofte kalt en "transpilerer". Dette betyr at du gir Babel JavaScript-kode og Babel endrer koden og genererer ny kode ut igjen.
## AST(Abstrakte-Syntaks-Tre)
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Ulike Stadier av Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Analyse
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Leksikal Analyse
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntaks Analyse
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transformasjon
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generering
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversering
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Gjester
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Baner
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Besøksbaner til gjester
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Tilstand
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Sikte
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindinger
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definisjoner
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Byggeklosser
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validatorer
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Formaterere
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Å skrive din første Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformasjon operasjoner
## Besøking
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulasjon
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Sikte
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin alternativer
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Å bygge noder
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Gode øvelser
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Unngå å traversere AST-en så mye som mulig
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Kombinere gjester når det er mulig
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Ikke bruk traversering når det kan gjøres manuelt
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimalisere nestede gjester
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Vær oppmerksom på struktur og hvordan den er bygd opp
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/no/user-handbook.md
================================================
# Babel User Handbook
This document covers everything you ever wanted to know about using [Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduksjon](#toc-introduction)
* [Setting up Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Introduksjon
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can use (and create) the next generation of JavaScript, as well as the next generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Setting up Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
================================================
FILE: translations/pl/README.md
================================================
# Babel Podręcznik
Ten podręcznik jest podzielony na dwie części:
* [Podręcznik Użytkownika](user-handbook.md) - Jak zainstalować/skonfigurować Babel i podobne.
* [Podręcznik o Wtyczkach](plugin-handbook.md) - Jak stworzyć wtyczki dla Babel.
> Po więcej informacji, śledź [@thejameskyle](https://twitter.com/thejameskyle) na Twitterze.
Jeśli czytasz tłumaczenie tego podręcznika, nadal możesz znaleźć fragmenty po angielsku, które nie były jeszcze przetłumaczone. Jeśli chcesz przyłączyć się do tłumaczenia, musisz zrobić do przez platformę Crowdin. Przeczytaj proszę [wytyczne o współpracy](/CONTRIBUTING.md) po więcej informacji. Znajdziesz tutaj wiele angielskich słów, które są pojęciami w programowaniu. Jeśli były by one przetłumaczone na inny język, wystąpił by brak konsekwencji i zrozumieniu przy czytaniu o nich. W wielu przypadkach, znajdziesz dosłowne tłumaczenie i następujące po nim angielskie wyrażenie w nawiasach `()`. Na przykład: drzewo składniowe (ASTs).
================================================
FILE: translations/pl/plugin-handbook.md
================================================
# Babel Plugin Podręcznik
This document covers how to create [Babel](https://babeljs.io) [plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Table of Contents
* [Introduction](#toc-introduction)
* [Basics](#toc-basics)
* [ASTs](#toc-asts)
* [Stages of Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Lexical Analysis](#toc-lexical-analysis)
* [Syntactic Analysis](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Writing your first Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introduction
Babel is a generic multi-purpose compiler for JavaScript. More than that it is a collection of modules that can be used for many different forms of static analysis.
> Static analysis is the process of analyzing code without executing it. (Analysis of code while executing it is known as dynamic analysis). The purpose of static analysis varies greatly. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
You can use Babel to build many different types of tools that can help you be more productive and write better programs.
> ***Po więcej informacji, śledź [@thejameskyle](https://twitter.com/thejameskyle) na Twitterze.***
* * *
# Basics
Babel is a JavaScript compiler, specifically a source-to-source compiler, often called a "transpiler". This means that you give Babel some JavaScript code, Babel modifies the code, and generates the new code back out.
## ASTs
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Stages of Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Parse
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntactic Analysis
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transform
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Writing your first Babel Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/pl/user-handbook.md
================================================
# Podręcznik Użytkownika Babel
Ten dokument obejmuje wszystko, co chciałbyś wiedzieć o [Babelu](https://babeljs.io) i powiązanych narzędziach.
[](http://creativecommons.org/licenses/by/4.0/)
Niniejszy podręcznik jest dostępny w innych językach. Zobacz [plik README](/README.md), aby zobaczyć pełną listę.
# Spis treści
* [Wprowadzenie](#toc-introduction)
* [Instalacja Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Uruchamianie Babel CLI z poziomu projektu](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Konfigurowanie Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Wykonywanie kodu wygenerowanego przez Babel](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Konfigurowanie Babel (Zaawansowane)](#toc-configuring-babel-advanced)
* [Ręczny wybór wtyczek](#toc-manually-specifying-plugins)
* [Opcje wtyczek](#toc-plugin-options)
* [Dostosowywanie Babel w oparciu o środowisko](#toc-customizing-babel-based-on-environment)
* [Tworzenie własnych ustawień](#toc-making-your-own-preset)
* [Babel i inne narzędzia](#toc-babel-and-other-tools)
* [Narzędzia analizy statycznej](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Styl kodu](#toc-code-style)
* [Dokumentacja](#toc-documentation)
* [Frameworki](#toc-frameworks)
* [React](#toc-react)
* [Edytory tekstu i IDE](#toc-text-editors-and-ides)
* [Wsparcie Babel](#toc-babel-support)
* [Forum Babel](#toc-babel-forum)
* [Czat Babel](#toc-babel-chat)
* [Błędy Babel](#toc-babel-issues)
* [Tworzenie dobrego raportu o błędzie](#toc-creating-an-awesome-babel-bug-report)
# Wprowadzenie
Babel jest ogólnym, wielozadaniowym kompilatorem JavaScriptu. Korzystając z Babela, otwierasz sobie drogę do możliwości korzystania (i tworzenia) nowoczesnego kodu w oparciu o przyszłe wydania JavaScriptu, a także otrzymujesz dostęp do najnowocześniejszych rozwiązań języka.
JavaScript stale się rozwija. Nowe specyfikacje, propozycje oraz możliwości pojawiają się w zastraszającym tempie. Używając Babela, możesz korzystać z tych wszystkich udogodnień już teraz - lata przed tym, nim staną się powszechną praktyką.
Babel daje taką możliwość dzięki kompilowaniu kodu JavaScriptu, napisanego zgodnie z najnowszymi standardami, do postaci, która jest kompatybilna z dzisiejszymi interpreterami. Proces ten jest nazywany jako kompilowanie źródła-do-źródła (ang. source-to-source) czy powszechniej "transpiling".
Na przykład Babel może przekształcić nową składnię funkcji ES2015 z takiej:
```js
const square = n => n * n;
```
Do następującej:
```js
const square = function square(n) {
return n * n;
};
```
Babel daje oczywiście wiele innych możliwości. Wspiera między innymi składnię JSX dostępną w React oraz składnię Flow dla statycznego sprawdzania zgodności typów.
Ponadto, każdy komponent Babela jest wtyczką (ang. plugin). Każdy, w dowolnej chwili, może stworzyć własną wtyczkę, korzystając z całej mocy oferowanej przez Babel.
*Idąc dalej podjętą retoryką*, struktura Babela została rozbita do postaci kilku głównych modułów. Dzięki nim, możliwe jest tworzenie nowoczesnego kodu JavaScript.
Wielu ludzi już zaczęło to robić. Społeczność, która urosła wokół Babela, jest naprawdę piorunująca i różnorodna. W tym podręczniku omówię narzędzia wbudowane w Babela, ale skupię się także na wtyczkach stworzonych przez naszą społeczność.
> ***Aby uzyskać więcej informacji na ten temat, sprawdź wpisy na Twitterze.***
* * *
# Instalacja Babel
Biorąc pod uwagę, że społeczność JavaScriptu nie skupia się wyłącznie wokół jednego taskera, frameworka, platformy itp., Babel udostępnia oficjalne wsparcie do integracji z różnymi, wiodącymi narzędziami. Wszystko, począwszy od Gulpa i kończąc na Browserify; od Embera po Meteora; nie ma znaczenia jak wygląda twoje środowisko pracy - prawdopodobnie i tak posiada ono oficjalne wsparcie.
Na potrzeby tego podręcnzika, omówimy standardowe metody na instalację Babela. Możesz takze odwiedzić bardziej interaktywny [generator instalatora](http://babeljs.io/docs/setup), gdzie dostępne są wszelkie możliwe metody integracji do twoich potrzeb.
> **Uwaga:** Ten poradnik nie będzie tłumaczył jak działają polecenia linii komend takie jak `node` lub `npm`. Autor poradnika zakłada, że jesteś już zaznajomiony z tymi technologiami.
## `babel-cli`
CLI Babela jest najprostszą metodą na skompilowanie plików do postaci Babela bezpośrednio z linii poleceń.
Zainstalujmy go globalnie, aby poznać podstawowe zasady jego działania.
```sh
$ npm install --global babel-cli
```
Możemy podjąć naszą pierwszą próbę kompilacji w następujący sposób:
```sh
$ babel my-file.js
```
Zwróci to skompilowaną zawartość pliku bezpośrednio do okienka terminala. Aby zapisać zawartość do pliku, musimy dopisać `--out-file` lub `-o`.
```sh
$ babel example.js --out-file compiled.js
# lub
$ babel example.js -o compiled.js
```
Jeśli chcielibyśmy skompilować cały katalog, aby pliki wynikowe przenieść do innego katalogu, możemy to zrobić korzystając z paremetru `--out-dir` lub `-d`.
```sh
$ babel src --out-dir lib
# lub
$ babel src -d lib
```
### Uruchamianie Babel CLI z poziomu projektu
O ile *możliwe jest* zainstalowanie CLI Babela globalnie, bardziej preferowaną formą jest zainstalowanie go **lokalnie** dla każdego projektu.
Powody tego są dwa:
1. Różne projekty na tym samym komputerze mogą bazowac na innych wersjach Babela. Dzięki temu istnieje możliwość aktualizowania ich z osobna.
2. Oznacza to, że nie będziesz musiał zgadywać, jakie składowe zostały już zainstalowane bezpośrednio na twoim środowisku. Dzięki temu twój projekt staje się bardziej "przenośny".
Można zainstalować Babela lokalnie wywołując polecenie:
```sh
$ npm install --save-dev babel-cli
```
> **Uwaga:** Biorąc pod uwagę, że uruchamianie Babela globalnie nie jest dobrym pomysłem, możesz chcieć odinstalować globalną kopię wywołując polecenie:
>
> ```sh
$ npm uninstall --global babel-cli
```
Kiedy instalacja zakończy się, plik `package.json` powinien wyglądać następująco:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Konfigurowanie Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Wykonywanie kodu wygenerowanego przez Babel
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Konfigurowanie Babel (Zaawansowane)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Ręczny wybór wtyczek
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Opcje wtyczek
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Dostosowywanie Babel w oparciu o środowisko
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Tworzenie własnych ustawień
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel i inne narzędzia
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Narzędzia analizy statycznej
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Styl kodu
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Dokumentacja
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworki
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Edytory tekstu i IDE
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Wsparcie Babel
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Forum Babel
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Czat Babel
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Błędy Babel
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Tworzenie dobrego raportu o błędzie
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***Po więcej informacji, śledź [@thejameskyle](https://twitter.com/thejameskyle) na Twitterze.***
================================================
FILE: translations/pt-BR/README.md
================================================
# Babel Handbook
Este manual está dividido em duas partes:
* [Manual do usuário](user-handbook.md) - como configurar o Babel e muito mais.
* [Manual de plugins](plugin-handbook.md) - como criar plugins para o Babel.
> Para futuras atualizações, siga [@thejameskyle](https://twitter.com/thejameskyle) no Twitter.
Se você está lendo uma tradução do Inglês deste manual, você pode encontrar seções inglês que ainda não foram traduzidas. Se você quiser contribuir com uma das traduções, você deve fazê-lo através do Crowdin. Por favor, leia as [orientações de contribuição](/CONTRIBUTING.md) para obter mais informações. Você encontrará algumas palavras em inglês que são conceitos de programação. Se estas foram traduzidas para outras línguas haveria uma falta de consistência e fluência ao ler. Em muitos casos, você irá encontrar a tradução literal, seguida do termo inglês em parênteses `()`. Por exemplo: árvores de sintaxe abstrata (ASTs).
================================================
FILE: translations/pt-BR/plugin-handbook.md
================================================
# Babel Plugin Handbook
Este documento aborda como criar [plugins](https://babeljs.io/docs/advanced/plugins/) para o [Babel](https://babeljs.io).
[](http://creativecommons.org/licenses/by/4.0/)
Este manual está disponível em outros idiomas, consulte o [arquivo Leia-me](/README.md) para obter uma lista completa.
# Tabela de Conteúdos
* [Introdução](#toc-introduction)
* [Noções básicas](#toc-basics)
* [ASTs](#toc-asts)
* [Estágios do Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Análise léxica](#toc-lexical-analysis)
* [Análise sintática](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definições](#toc-definitions)
* [Construtores](#toc-builders)
* [Validadores](#toc-validators)
* [Conversores](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Escrevendo seu primeiro Plugin do Babel](#toc-writing-your-first-babel-plugin)
* [Operações de transformação](#toc-transformation-operations)
* [Visitando](#toc-visiting)
* [Obtendo o caminho do sub-nó](#toc-get-the-path-of-a-sub-node)
* [Verificando se um nó possui um certo tipo](#toc-check-if-a-node-is-a-certain-type)
* [Verificando se o caminho é de um tipo específico](#toc-check-if-a-path-is-a-certain-type)
* [Verificando se um identificador é referenciado](#toc-check-if-an-identifier-is-referenced)
* [Encontre o caminho específico de um parente](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulação](#toc-manipulation)
* [Substituindo um nó](#toc-replacing-a-node)
* [Substituindo um nó com vários nós](#toc-replacing-a-node-with-multiple-nodes)
* [Substituindo um nó com um valor de string](#toc-replacing-a-node-with-a-source-string)
* [Inserindo um nó irmão](#toc-inserting-a-sibling-node)
* [Colocando em um container](#toc-inserting-into-a-container)
* [Removendo o nó pai](#toc-removing-a-node)
* [Substituindo um nó pai](#toc-replacing-a-parent)
* [Removendo o nó pai](#toc-removing-a-parent)
* [Escopo](#toc-scope)
* [Verificando se uma variável local está vinculada](#toc-checking-if-a-local-variable-is-bound)
* [Gerando um UID](#toc-generating-a-uid)
* [Empurrando uma declaração de variável para um escopo pai](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Renomear um binding e suas referências](#toc-rename-a-binding-and-its-references)
* [Opções do plugin](#toc-plugin-options)
* [Antes e Depois em Plugins](#toc-pre-and-post-in-plugins)
* [Habilitando Sintaxe em Plugins](#toc-enabling-syntax-in-plugins)
* [Construindo nós](#toc-building-nodes)
* [Melhores práticas](#toc-best-practices)
* [Evitar cruzar o máximo possível o AST](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Mesclar os visitantes sempre que possível](#toc-merge-visitors-whenever-possible)
* [Não cruzar quando farão pesquisa manual](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Otimizando os visitantes aninhados](#toc-optimizing-nested-visitors)
* [Estando ciente das estruturas aninhadas](#toc-being-aware-of-nested-structures)
* [Teste Unitário](#toc-unit-testing)
# Introdução
Babel é um compilador genérico de múltiplos propósito para JavaScript. Mais do que isso, é uma coleção de módulos que podem ser usados de muitas formas diferentes de análise estática.
> Análise estática é o processo de análise de código sem executá-lo. (Análise de código durante a execução é conhecido como análise dinâmica). O objectivo da análise estática varia muito. Ele pode ser usado para linting, compilação, realce de código, transformação de código, otimização, minificação e muito mais.
Você pode usar Babel para construir vários tipos de ferramentas que podem ajudá-lo a ser mais produtivo e escrever programas melhores.
> ***Para futuras atualizações, siga [@thejameskyle](https://twitter.com/thejameskyle) no Twitter.***
* * *
# Noções básicas
Babel é um compilador JavaScript, especificamente um compilador de código para código, muitas vezes chamado de "transpiler". Isto significa que você dá ao Babel algum código JavaScript, ele modifica o código e gera um novo código de volta.
## ASTs
Cada um destes passos envolvem criação ou trabalho com uma [Árvore de Sintaxe Abstrata](https://en.wikipedia.org/wiki/Abstract_syntax_tree) ou AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Confira [Explorando a AST](http://astexplorer.net/) para ter uma melhor noção de nós de AST. [Aqui](http://astexplorer.net/#/Z1exs6BWMq) está um link para ele, com o código de exemplo acima.
Este mesmo programa pode ser representado como uma lista assim:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Ou como um JSON como este:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Você observará que cada nível da AST tem uma estrutura semelhante:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Nota: Algumas propriedades foram removidas para manter a simplicidade.
Cada um deles são conhecidos como um **nó**. Um AST pode ser composta por um único nó, ou centenas, senão milhares de nós. Juntos, eles são capazes de descrever a sintaxe de um programa que pode ser usado para análise estática.
Cada nó tem esta interface:
```typescript
interface Node {
type: string;
}
```
O `tipo` de campo é uma seqüência de caracteres que representa o tipo do nó do objeto (ex. `"FunctionDeclaration"`, `"Identifier"` ou `"BinaryExpression"`). Cada tipo de nó define um conjunto adicional de propriedades que descrevem o tipo de nó específico.
Existem propriedades adicionais em cada nó que Babel gera que descrevem a posição do nó no código-fonte original.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Estas propriedades `start`, `end`, `loc`, aparecem em cada nó.
## Estágios do Babel
Os três estágios primários de Babel são **parse**, **transform**, **generate**.
### Parse
O estágio de **parse**, paga o código e produz um AST. Existem duas fases de análise de Babel: [**Análise léxica**](https://en.wikipedia.org/wiki/Lexical_analysis) e [**Análise sintática**](https://en.wikipedia.org/wiki/Parsing).
#### Análise léxica
Análise lexical pega uma sequência de caracteres de código e o transforma em um fluxo de **tokens**.
Você pode pensar nos tokens como uma matriz plana de pedaços de sintaxe de linguagem.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } }, ...
]
```
Cada um dos `tipos` tem um conjunto de propriedades que descrevem o token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Como nós de AST também têm um `start`, `end` e `loc`.
#### Análise sintática
Análise sintática irá levar a um fluxo de tokens e transformá-lo em uma representação de AST. Usando as informações de tokens, esta fase será reformatá-los como um AST que representa a estrutura do código de uma forma que torna mais fácil trabalhar.
### Transform
O estágio de [transform](https://en.wikipedia.org/wiki/Program_transformation) atravessa um AST, adicionando, atualizando e removendo os nós junto. Esta é de longe a parte mais complexa de Babel ou qualquer compilador. Isto é onde operam os plugins e então será o assunto principal deste manual. Então não mergulharemos tão profundamente agora.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Geração de código é muito simples: você percorre através da AST primeiro, e constroi uma sequência de caracteres que representa o código transformado.
## Traversal
Quando você deseja transformar um AST... você tem que [percorrer (traverse) a árvore](https://en.wikipedia.org/wiki/Tree_traversal) recursivamente.
Digamos que temos um nó do tipo `FunctionDeclaration`. Ele tem algumas propriedades como: `id`, `params` e `body`. Cada um deles possui seus próprios nós aninhados.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Então começamos no nó `FunctionDeclaration` e sabemos suas propriedades internas, logo, podemos visitar cada uma delas e seus filhos na sequência.
Depois vamos para o `id`, que é um `Identifier`.`Identifier` e não tem quaisquer propriedades do nó filho então podemos continuar.
Depois disso temos a propriedade `params`, que é uma matriz de nós. Nós os percorreremos, visitando cada um deles. Neste caso `params` possui um único nó, que é também um <0>Identifier0>, então podemos continuar.
Então encontramos o `body` que é um `BlockStatement` com uma propriedade `body` que é uma matriz de nós, então nós vamos a cada um deles.
O único item que aqui é um nó `ReturnStatement` que tem um `argument`, nós vamos para o `argument` e encontramos um `BinaryExpression`.
O `BinaryExpression` tem uma propriedade `operator`, uma `left` e uma `right`. O operador não é um nó, apenas um valor, então não vamos até ele. Em vez disso, apenas visitamos as propriedades `left` e `right`.
Este processo de percorrer (traverse) acontece durante toda a fase de transformação de Babel.
### Visitors
Quando falamos sobre "ir" para um nó, na verdade queremos dizer que estamos **visitando** eles. A razão pela qual nós usamos esse termo está ligada ao conceito do design pattern [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern), usado frequentemente.
Visitors é um padrão usando pela AST para percorrer a linguagem. Simplificando, eles são um objeto com métodos definidos para aceitar tipos específicos de nó em uma árvore. Isso é um pouco abstrato, então vamos ver um exemplo.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// Você também pode criar um Visitor e adicionar métodos depois
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Nota:** `Identifier() { ... }` é uma abreviação para `Identifier: {enter() { ... }}`.
Esta é uma visita básica que quando usado durante um percurso irá chamar o método `Identifier()` para cada `Identifier` na árvore.
Então com esse código o método `Identifier()` será chamado quatro vezes, um para cada `Identifier` (incluindo a função `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
Essas chamadas acontecem no nó de **entrada**. No entanto, há também a possibilidade de chamar um método visitante quando estiver no nó de **saída**.
Imagine que temos esta estrutura de árvore:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
Enquanto percorrermos cada ramo da árvore, eventualmente atingimos becos sem saída, onde precisamos voltar para a árvore anterior para obter o próximo nó. Percorrendo a árvore podemos temos os nós de **entrada**, e voltando um nível acima, temos os nós de **saída** de cada nó.
Vamos *caminhar* através deste processo, utilizando a árvore acima.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
Então, quando criamos um visitante, temos duas oportunidades para visitar um nó.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
Por exemplo,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? Podemos ter um enorme objeto mutável, no qual você pode manipular e ter total acesso, ou, podemos simplificar isso com os **Paths**.
Um **Path**, é um objeto que representa a ligação entre dois nós.
Por exemplo, se tomarmos o seguinte nó e seu filho:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
E representar o `Indentifier` do nó filho como um caminho, parece algo como isto:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. Desta forma, você está trabalhando, principalmente, com a representação reativa de um nó em vez do próprio nó.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
Estado (global e local) são os inimigos da AST. Eles irão pregar tanta peça em você, que você vai acabar vendo que seus conhecimentos sobre estado estarão errados devido a alguma sintaxe que você não considerava.
Veja o seguinte código:
```js
function square(n) {
return n * n;
}
```
Vamos escrever um Visitor que vai renomear `n` para `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Nota: Este não é um substituto para a documentação da API, que será mais detalhada, e que estarão disponível em outro lugar em breve.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Nota:** `sourceType` tem como padrão o modo `"script"` e retorna um erro quando ele encontra declarações `import` ou `export`. Use `sourceType: "module"` para se livrar desses erros.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definições
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Construtores
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validadores
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Conversores
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Escrevendo seu primeiro Plugin do Babel
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Operações de transformação
## Visitando
### Obtendo o caminho do sub-nó
Para acessar a propriedade de um nó AST, você normalmente acessa o nó e, em seguida, a propriedade. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
Se você precisar acessar o `path` dessa propriedade, em vez disso, use o método `get` do path, passando em string para a propriedade.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Verificando se um nó possui um certo tipo
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Verificando se o caminho é de um tipo específico
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Verificando se um identificador é referenciado
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Encontre o caminho específico de um parente
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulação
### Substituindo um nó
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Substituindo um nó com vários nós
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Substituindo um nó com um valor de string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserindo um nó irmão
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Colocando em um container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removendo o nó pai
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Substituindo um nó pai
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removendo o nó pai
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Escopo
### Verificando se uma variável local está vinculada
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Gerando um UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Empurrando uma declaração de variável para um escopo pai
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Renomear um binding e suas referências
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Opções do plugin
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Antes e Depois em Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Habilitando Sintaxe em Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Construindo nós
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Melhores práticas
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Evitar cruzar o máximo possível o AST
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Mesclar os visitantes sempre que possível
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Não cruzar quando farão pesquisa manual
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Otimizando os visitantes aninhados
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Estando ciente das estruturas aninhadas
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Teste Unitário
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***Para futuras atualizações, siga [@thejameskyle](https://twitter.com/thejameskyle) e [@babeljs](https://twitter.com/babeljs) no Twitter.***
================================================
FILE: translations/pt-BR/user-handbook.md
================================================
# Manual do Usuário do Babel
Este documento abrange tudo o que você sempre quis saber sobre a utilização do [Babel](https://babeljs.io) e ferramentas relacionadas.
[](http://creativecommons.org/licenses/by/4.0/)
Este manual está disponível em outros idiomas, consulte o [arquivo Leia-me](/README.md) para obter uma lista completa.
# Tabela de Conteúdos
* [Introdução](#toc-introduction)
* [Configurando o Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Usando o "babel-cli" em um projeto](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configurando o Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-state-x`](#toc-babel-preset-stage-x)
* [Executando o código gerado pelo Babel](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configurando Babel (Avançado)](#toc-configuring-babel-advanced)
* [Adicionando plugins manualmente](#toc-manually-specifying-plugins)
* [Opções de configurações dos plugins](#toc-plugin-options)
* [Personalizando Babel com variáveis de ambiente](#toc-customizing-babel-based-on-environment)
* [Criando suas próprias predefinições (presets)](#toc-making-your-own-preset)
* [Babel e outras ferramentas](#toc-babel-and-other-tools)
* [Ferramentas de análise estática](#toc-static-analysis-tools)
* [Usando Eslint](#toc-linting)
* [Usando JSCS](#toc-code-style)
* [Documentação](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [IDE's e editores de texto](#toc-text-editors-and-ides)
* [Procurando ajuda](#toc-babel-support)
* [Fóruns](#toc-babel-forum)
* [Salas de bate-papo](#toc-babel-chat)
* [Reportando problemas](#toc-babel-issues)
* [Criando relatórios de bug incríveis](#toc-creating-an-awesome-babel-bug-report)
# Introdução
Babel é um compilador genérico multifuncional de JavaScript. Usando o Babel, você pode usar (e criar) a próxima geração do JavaScript, além da próxima geração da ferramenta do JavaScript.
JavaScript é uma linguagem que está em constante desenvolvimento, com novas especificações e propostas vindas com novas funcionalidades o tempo todo. A utilização do Babel vai lhe auxiliar na utilização de muitas funcionalidades muito antes de elas estarem disponíveis de forma abrangente.
O Babel faz isso compilando o código JavaScript escrito com os últimos padrões em uma versão que funcionará de forma abrangente. Esse processo é conhecido como compilação de código para código, também conhecido como transpiling em inglês.
O Babel pode transformar, por exemplo, a nova sintaxe de função de seta do ES2015 disso:
```js
const square = n => n * n;
```
Para isso:
```js
const square = function square(n) {
return n * n;
};
```
No entanto, Babel pode oferecer muito mais do que isso, já que o Babel tem suporte a extensão de sintaxe, como a sintaxe JSX para React e o suporte a sintaxe Flow para avaliação de tipo estático.
Além disso, tudo no Babel é simplesmente um plugin e qualquer um pode criar seu próprio plugin usando a força do Babel para fazer o que quiser.
*Muito além* disso, Babel é formado por um número de módulos principais que qualquer um pode usar para construir a próxima geração da ferramenta JavaScript.
E muitas pessoas o fazem, por isso, surgiu um enorme e diversificado ecossistema de ferramentas em torno do Babel. Em todo esse manual, vamos aborda as ferramentas embutidas no Babel e também alguns recursos úteis da comunidade.
> ***Para futuras atualizações, siga [@thejameskyle](https://twitter.com/thejameskyle) no Twitter.***
* * *
# Configurando o Babel
Já que a comunidade JavaScript não possui uma única ferramenta de construção, framework, plataforma, etc., Babel possui integrações oficiais para todas as maiores ferramentas. Tudo desde Gulp até Browserify, de Ember até Meteor, não importa como está sua configuração, existe provavelmente uma integração oficial.
Para mérito desse livro, vamos cobrir apenas os métodos padrões já embutidos no Babel para integração, mas você pode visitar a [página de configuração interativa](http://babeljs.io/docs/setup) para visualizar todas as integrações.
> **Nota:** Esse guia usa ferramentas de linha de comando como `node` e `npm`. Antes de seguir adiante, você deve familiarizar-se com essas ferramentas.
## `babel-cli`
O Cliente do Babel é uma maneira simples de compilar arquivos com o Babel na linha de comando.
Vamos primeiramente instalá-lo globalmente para aprender o básico.
```sh
$ npm install --global babel-cli
```
Nós podemos compilar nosso primeiro arquivo assim:
```sh
$ babel my-file.js
```
Isso irá colocar colocar o arquivo de saída compilado diretamente em seu terminal. Para escrevê-lo em um arquivo, nós iremos especificar um arquivo de saída utilizando a opção `--out-file` ou `-o`.
```sh
$ babel example.js --out-file compiled.js
# ou
$ babel example.js -o compiled.js
```
Se quisermos compilar um diretório inteiro em uma novo diretório, nós podemos fazê-lo com `--out-dir` ou `-d`.
```sh
$ babel src --out-dir lib
# ou
$ babel src -d lib
```
### Executando o Babel CLI em um projeto
Embora você *possa* instalar o Cliente do Babel globalmente na sua máquina, é mais recomendável fazer a instalação **localmente** projeto por projeto.
Há duas razões principais para isso:
1. Diferentes projetos na mesma máquina podem depender de versões diferentes do Babel, o que lhe possibilita atualizar um projeto de cada vez.
2. Isso significa que você não tem uma dependência implícita do ambiente em que está trabalhando. Isso torna seu projeto mais portável e fácil de configurar.
Nós podemos instalar o Cliente do Babel localmente com o seguinte comando:
```sh
npm install --save-dev babel-cli
```
> **Nota:** Como geralmente é uma má idéia executar Babel globalmente, você pode querer desinstalar a cópia global executando:
>
> ```sh
$ npm uninstall --global babel-cli
```
Quando o procedimento terminar, o arquivo `package.json` deve ficar assim:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Agora ao invés de rodar o Babel diretamente da linha de comando, nós iremos colocar nossos comandos no **npm scripts**, o qual irá usar nossa versão local.
Adicione o campo `"scripts"` no seu `package.json` e coloque o comando dentro da chave `"build"`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Agora, do nosso terminal, podemos usar:
```js
npm run build
```
Isso irá rodar o Babel da mesma maneira como no exemplo anterior, porém, agora usando a versão local.
## `babel-register`
O próximo jeito mais comum de rodar o Babel é através do `babel-register`. Essa opção vai permitir que você use Babel em tempo de execução, o que facilita a integração no seu ambiente.
Nota, isso não é recomendado para uso em produção. Usar código que é compilado em tempo de execução é uma má prática. É muito melhor compilar seu código antes de enviar para produção. Porém, dessa maneira, fica fácil criar e testar scripts localmente.
Primeiro, vamos criar nosso arquivo `index.js` em nosso projeto.
```js
console.log("Hello world!");
```
Se rodarmos nosso arquivo, `node index.js`, ele não será compilado por Babel. Ao invés disso, precisamos adicionar `babel-register`.
Primeiro, vamos instalar `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Agora, vamos criar o arquivo `register.js` no projeto, e escrever o seguinte:
```js
require("babel-register");
require("./index.js");
```
O que esse arquivo faz é registrar o Babel no sistema de módulos do Node, com isso, ele compila qualquer arquivo que seja importado usando *required*.
E agora, ao invés de usarmos `node index.js`, vamos usar `register.js`.
```sh
$ node register.js
```
> **Nota:** Você não pode registrar o Babel no mesmo arquivo que você deseja compilar. O sistema de módulos do Node vai executar o arquivo antes do Babel compilá-lo.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
Se você estiver executando algum código pela CLI do `node`, a maneira mais fácil de integrar Babel é utilizar a CLI `babel-node`, que, em grande parte, é um ótimo substituto para a CLI do `node`.
Nota, isso não é recomendado para uso em produção. Usar código que é compilado em tempo de execução é uma má prática. É muito melhor compilar seu código antes de enviar para produção. Porém, dessa maneira, fica fácil criar e testar scripts localmente.
Primeiramente, tenha certeza que você tem `babel-cli` instalado.
```sh
npm install --save-dev babel-cli
```
> **Nota:** Se você está se perguntando por que estamos instalando isso localmente, leia a seção acima, [executando a CLI do Babel dentro de um projeto](#toc-running-babel-cli-from-within-a-project).
Em seguida, substitua todas as declarações `node` por `babel-node`.
Se você estiver usando npm `scripts`, você pode simplesmente fazer:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Caso contrário, você precisará declarar o caminho completo para o `babel-node`.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Dica: Você também pode usar o [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
Se por algum motivo, você precisar usar o Babel programaticamente, você pode usa-lo através do pacote `babel-core`.
Primeiro, instale o `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
Se você tiver uma string, você pode compila-la diretamente usando `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
Se você estiver trabalhando com arquivos, você também pode usar a Api assíncrona:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Ou a Api síncrona:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
Se você já tiver uma Babel AST, você pode transforma-la diretamente usando.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
Para todos os métodos acima, o parâmetro, `options`, refere-se ao: https://babeljs.io/docs/usage/api/#options.
* * *
# Configurando o Babel
Agora você já deve ter notado que, usando Babel por conta própria, parece que estamos apenas copiando arquivos de um local para o outro.
Isso é porque não falamos para o Babel o que ele deve fazer.
> Babel é um compilador de propósitos gerais, que é usado em uma infinidade de maneiras diferentes, ele não faz nada por padrão. Você deve, explicitamente, dizer o que o Babel deve fazer.
Você pode dar instruções sobre o que o Babel deve fazer, através da instalação de **plugins** ou **presets** (grupo de plugins).
## `.babelrc`
Antes de começar a falar para o Babel, o que ele deve fazer. Precisamos criar um arquivo de configuração. Tudo o que você precisa fazer é criar o arquivo `.babelrc` na raíz do seu projeto. Começando assim:
```js
{
"presets": [],
"plugins": []
}
```
Essa é a maneira de configurar o Babel e dizer o que ele deve fazer.
> **Nota:** Você também pode passar opções para o Babel de outras formas, porém, a melhor maneira é utilizar a convenção do arquivo `.babelrc`.
## `babel-preset-es2015`
Vamos começar falando para o Babel compilar ES2015 (a nova versão do JavaScript, também conhecida como ES6) para ES5 (a versão disponível na maioria dos ambientes de hoje em dia).
Vamos fazer isso através da instalação do preset "es2015":
```sh
$ npm install --save-dev babel-preset-es2015
```
Em seguida, vamos modificar nosso arquivo `.babelrc` para incluir o preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Configurar o React é tão fácil como. Basta instalar o preset:
```sh
$ npm install --save-dev babel-preset-react
```
E adicionar o preset no seu arquivo `.babelrc`:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
Existem algumas propostas de padrões e funcionalidades em processo para futuras versões do JavaScript, definidas através do TC39 (comitê técnico responsável pelo padrão ECMAScript).
Esse processo contém 5 etapas (0-4). Conforme as propostas vão ganhando mais força e são propensas a serem aceitas como novos padrões, elas vão subindo de estágios até serem finalmente aceitas na etapa 4.
Essas etapas estão agrupadas no Babel em 4 diferentes presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Observe que não há nenhum estágio 4, que é simplesmente o novo padrão já contido no preset `es2015` acima.
Cada um desses presets precisam do preset do seu estágio posterior, o seja, `babel-preset-stage-1` precisa do `babel-preset-stage-2` que precisa do `babel-preset-stage-3`.
Para utiliza-lo, simplesmente instale o preset que você deseja usar:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Em seguida, você pode adicioná-lo no seu arquivo `.babelrc`.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executando o código gerado pelo Babel
Então você compilou seu código com Babel? Bom, esse não é o final da história.
## `babel-polyfill`
Quase todas as propostas para futuras funcionalidades do JavaScript podem ser compiladas pelo Babel, porém, o mesmo não é verdade para suas APIs.
Por exemplo, o código a seguir faz uso das "arrow functions", e precisa ser compilado:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Que o transforma em:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
No entando, ele ainda não vai funcionar em todos os lugares porque `Array.from` não existe em todos os ambientes JavaScript.
Uncaught TypeError: Array.from is not a function
Para resolver esse problema, precisamos usar um [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Resumindo, um polyfill é um pedaço de código que replica uma Api nativa, caso ela não exista na execução atual. Permitindo que você use APIs como `Array.from` antes de estarem disponíveis.
Babel usa o excelente [core-js](https://github.com/zloirock/core-js) como seu polyfill, juntamente com uma versão customizada do [regenerator](https://github.com/facebook/regenerator), que permite a utilização de "generators" e "async functions".
Para usar o polyfill do Babel, simplesmente instale-o através do npm:
```sh
$ npm install --save babel-polyfill
```
E simplesmente o adicione no topo de todos os arquivos que irão precisar de polyfill:
```js
import "babel-polyfill";
```
## `babel-runtime`
Para implementar os detalhes das especificações do ECMAScript, Babel se utilizará de métodos "auxiliares" para manter o código gerado mais limpo.
Esses códigos auxiliares podem ser muito longos, e eles são adicionados no topo de cada arquivo. Para evitar esse trabalho, podemos incluir um único "runtime" que será utilizado, automáticamente, quando necessário.
Comece instalando `babel-plugin-transform-runtime` e `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Em seguida, atualize seu `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Agora, Babel irá compilar um código como esse:
```js
class Foo {
method() {}
}
```
Gerando:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Poupando o trabalho de colocar `_classCallCheck` e `_createClass` em cada arquivo onde são necessários.
* * *
# Configurando Babel (Avançado)
A maioria das pessoas, usam Babel apenas com seus presets padrões, porém, Babel expõe um controle muito maior que isso, vamos ver a seguir.
## Adicionando plugins manualmente
Os presets, são simplesmente, coleções de plugins pré-configurados, se você quiser fazer algo diferente, você deve especificar manualmente os plugins. Onde obtemos o funcionamento semelhante ao dos presets.
Primeiro, instale o plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Em seguida, adicione o campo `plugins` no seu `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
Isso lhe dá um controle mais refinado sobre as transformações que você deseja executar.
Para obter uma lista completa de plugins oficiais, consulte a [página de plugins do Babel](http://babeljs.io/docs/plugins/).
Dê uma olhada também, em todos os plugins que foram [construídos pela comunidade](https://www.npmjs.com/search?q=babel-plugin). Se você quer aprender como escrever seu próprio plugin leia o [Manual de plugins do Babel](plugin-handbook.md).
## Opções de configurações dos plugins
Muitos plugins também tem opções para configurá-los para um comportamento diferente. Por exemplo, muitas transformações têm um modo "loose", que remove algumas especificações em favor de um código final mais simples e de alto desempenho.
Para adicionar essas opções a um plugin, simplesmente faça a seguinte alteração:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> Vou estar trabalhando nas atualizações da documentação de plugins, melhorando os detalhes de cada opção nas próximas semanas. [Siga-me para saber mais](https://twitter.com/thejameskyle).
## Personalizando Babel com variáveis de ambiente
Os plugins do Babel resolvem muitas tarefas diferentes. Muitos deles são ferramentas de desenvolvimento que podem ajudá-lo a depurar seu código ou integrar com ferramentas. Há também um monte de plugins que são focados na otimização do seu código para produção.
Por esta razão, é comum querer configurar o Babel com base no seu ambiente. Você pode fazer isso facilmente com seu arquivo `.babelrc`.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel permitirá configurações dentro do `env`, com base no ambiente atual.
Você pode checar o ambiente atual usando pelo Babel com `process.env.BABEL_ENV`. Quando `BABEL_ENV` não estiver disponível, será usado `NODE_ENV` e, se também não estiver disponível, por padrão, será usado "development".
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Nota:** `[COMMAND]` é o que você irá usar para executar o Babel (ex: `babel`, `babel-node` ou talvez apenas `node`, caso você esteja usando algum atalho).
>
> **Dica:** Se você deseja que seu comando funcione em plataformas unix e windows, você pode usar [`cross-env`](https://www.npmjs.com/package/cross-env).
## Criando suas próprias predefinições (presets)
Especificando manualmente plugins? Opções de plugins? Configurações baseadas no ambiente? Toda esta configuração pode parecer como uma tonelada de repetição para todos os seus projetos.
Por este motivo, incentivamos a comunidade para criar suas próprias predefinições. Isto pode ser uma predefinição específica para a versão do [node](https://github.com/leebenson/babel-preset-node5) que estiver executando, ou talvez uma predefinição para [sua](https://github.com/cloudflare/babel-preset-cf) [empresa](https://github.com/airbnb/babel-preset-airbnb).
É fácil criar uma predefinição. Vamos dizer que você tenha este arquivo `.babelrc`:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
Tudo o que você precisa fazer é criar um novo projeto seguindo a convenção de nomenclatura `babel-preset-*` (por favor, seja responsável isso) e criar dois arquivos.
Primeiro, crie um novo arquivo `package.json` com as `dependências` necessárias para a sua predefinição.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Em seguida, crie um arquivo `index.js` que exporta o mesmo conteúdo do seu arquivo `.babelrc` em formato compatível com `módulos node`.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Então, simplesmente publique isso no npm e você pode usá-lo como faria com qualquer preset.
* * *
# Babel e outras ferramentas
Babel é bastante simples de instalar, uma vez que você pegar o jeito, mas, pode ser um pouco difícil de entender como configurá-lo com outras ferramentas. No entanto, tentamos trabalhar estreitamente com outros projetos para tornar a experiência mais simples possível.
## Ferramentas de análise estática
Padrões mais recentes trazem várias novidades para a sintaxe do JavaScript e ferramentas de análise estática estão apenas começando a tirar vantagem disso.
### Usando Eslint
Uma das ferramentas mais populares para linting é o [ESLint](http://eslint.org), por isso mantemos uma integração oficial [`babel-eslint`](https://github.com/babel/babel-eslint).
Primeiro, instale o `eslint` e `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Em seguida, crie ou use o arquivo `.eslintrc` existente em seu projeto e defina o `parser` como `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Agora adicione uma tarefa `lint` no seus npm scripts do `package.json`:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Em seguida, basta executar a tarefa, e você verá a mágica acontecer.
```sh
$ npm run lint
```
Para obter mais informações, consulte a documentação do [`babel-eslint`](https://github.com/babel/babel-eslint) ou [`eslint`](http://eslint.org).
### Usando JSCS
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS é uma ferramenta extremamente popular para levar o linting um passo adiante e verificar o estilo do próprio código. Um dos desenvolvedores responsável por manter o Babel e JSCS ([@hzoo](https://github.com/hzoo)), cuida da integração oficial com o JSCS.
E unindo ainda mais os projetos, essa integração agora faz parte do JSCS, utilizando a opção `--esnext`. Então, integrar o Babel é tão fácil quanto:
$ jscs . --esnext
E saindo da linha de comando, adicione a opção `esnext` no seu arquivo `.jscsrc`.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
Para obter mais informações, consulte a documentação do [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) ou [`jscs`](http://jscs.info).
### Documentação
Usando o Babel, ES2015 e Flow, você pode inferir muito sobre seu código. Usando [documentation.js](http://documentation.js.org) você pode gerar documentação detalhada de APIs muito facilmente.
Documentation.js se utiliza do Babel por debaixo dos panos para oferecer suporte a todas as sintaxes mais recente, incluindo anotações em Flow, que serve para declarar os tipos no seu código.
## Frameworks
Todos os principais frameworks JavaScript estão focados em alinhar suas APIs em torno do futuro da linguagem. Por causa disso, um grande trabalho tem sido feito em torno das ferramentas e processos de build.
Os frameworks têm a oportunidade não apenas de usar o Babel, mas também de extender suas funcionalidades e melhorar e experiência dos desenvolvedores.
### React
React mudou drasticamente sua API para alinhar com o sistema de class do ES2015 ([Leia mais sobre a atualização da API aqui](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). E ainda mais, React usa o Babel para compilar sua sintaxe JSX. Eles pararam de usar o compilador que eles criaram em favor do Babel. Você pode começar installando os pacotes `babel-preset-react`, seguindo as [instruções acima](#babel-preset-react).
A comunidade do React abraçou o Babel como parte de seu ecossistema, e hoje, cria ferramentas em cima disso. Há uma série de [pacotes de transformações criados pela comunidade](https://www.npmjs.com/search?q=babel-plugin+react).
Um dos mais notáveis, é o pacote [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform), que, junto com alguns [pacotes de transformações específicas para React](https://github.com/gaearon/babel-plugin-react-transform#transforms), podem habilitar processos como *hot module reloading* e utilitários para depuração.
## IDE's e editores de texto
Utilizar ES2015, JSX e Flow junto com Babel, pode trazer mais ânimo ao seu desenvolvimento, porém, se seu editor de texto não tem suporte para essas sintaxes, pode ser um problema manter-se produtivo. Por esse motivo, você vai querer configurar sua IDE ou editor de texto com o plugin para o Babel.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Procurando ajuda
Babel tem uma comunidade muito grande e que vem crescendo rapidamente, conforme crescemos, queremos assegurar que os desenvolvedores possuem todos os recursos que eles precisam para ter sucesso. Então, nós fornecemos diferentes canais para obter ajuda.
Lembre-se que, através de todas estas comunidades, temos um [Código de conduta](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md), e deve ser respeitado. Se você quebrar o código de conduta, medidas serão tomadas. Então por favor leia e se conscientize, para assim, interagir com os outros.
Também incentivamos que, como comunidade, podemos nos ajudar e tirar dúvidas um dos outros. Se você encontrar alguém fazendo uma pergunta, e você sabe a resposta, tire alguns minutos para ajudá-lo. Tente o seu melhor para ser gentil e ter empatia, quando fazê-lo.
## Fóruns
[ Discourse ](http://www.discourse.org) nos forneceu uma versão hospedada do seu software de fórum grátis (Nós os amamos por isso!). Se fóruns são para você, acesse [discuss.babeljs.io](https://discuss.babeljs.io).
## Salas de bate-papo
Todo mundo adora o [Slack](https://slack.com). Se você está procurando por apoio imediato da comunidade, venha conversar conosco no [slack.babeljs.io](https://slack.babeljs.io).
## Reportando problemas
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
Se você deseja reportar algo:
* [Procure pelo tema desejado](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Criando relatórios de bug incríveis
Os problemas listados no Babel, às vezes podem ser muito difíceis de depurar remotamente, então precisamos de toda a ajuda que conseguirmos. Gastar mais uns minutos para elaborar um relatório de bug realmente agradável, pode ajudar a ter seu problema resolvido significativamente mais rápido.
Primeiro, tente isolar o problema. É extremamente improvável que cada parte da sua instalação está contribuindo para o problema. Se seu problema é um pedaço de código de entrada de dados, tente excluir o máximo possível de código e manter o necessário que ainda cause o problema.
> [WIP]
* * *
> ***Para futuras atualizações, siga [@thejameskyle](https://twitter.com/thejameskyle) no Twitter.***
================================================
FILE: translations/pt-PT/README.md
================================================
# Manual de Babel
Este manual está dividido em duas partes:
* [Manual do utilizador](user-handbook.md) - Como configurar o Babel e mais.
* [Manual de Plugins](plugin-handbook.md) - Como criar plugins para o Babel.
> Para futuras atualizações, siga [@thejameskyle](https://twitter.com/thejameskyle) no Twitter.
Se está a ler uma tradução não inglesa deste manual é possível que ainda encontre algumas secções em inglês que ainda não foram traduzidas. Se gostava de contribuir para uma das traduções terá de o fazer através do Crowdin. Por favor leia as [instruções para contribuir](/CONTRIBUTING.md) para mais informações. Irá encontrar uma série de palavras em Inglês que são conceitos de programação. Se estas palavras fossem traduzidas para outras línguas seria dificil de manter a consistência e coêrencia ao ler sobre elas. Em muitos casos, você irá encontrar a tradução literal, seguida do termo inglês em parênteses`()`. Por exemplo: Árvores de Sintaxe Abstractas (Abstract Syntax Trees).
================================================
FILE: translations/pt-PT/plugin-handbook.md
================================================
# Manual de Plugins de Babel
Este documento explica como criar [plugins](https://babeljs.io/docs/advanced/plugins/) para o [Babel](https://babeljs.io).
[](http://creativecommons.org/licenses/by/4.0/)
Este manual está disponível noutras línguas. Uma lista completa de todas as traduções pode ser encontrada no [README](/README.md).
# Índice
* [Introdução](#toc-introduction)
* [Noções básicas](#toc-basics)
* [ASTs](#toc-asts)
* [Estágios de Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Lexical Analysis](#toc-lexical-analysis)
* [Syntactic Analysis](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [Estado](#toc-state)
* [Âmbitos](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definições](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Construir o teu primeiro plugin de Babel](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introdução
Babel é um compilador de múltiplos propósito genérico para JavaScript. Mais do que isso é uma coleção de módulos que podem ser usados de muitas formas diferentes de análise estática.
> Análise estática é o processo de análisar código sem o executar. (Análise de código durante a sua execução é conhecido como análise dinâmica). O objectivo da análise estática varia muito. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
Babel pode ser usado para construir vários tipos de ferramentas que podem ajudá-lo a ser mais produtivo e escrever programas melhores.
> ***Para futuras atualizações, siga [@thejameskyle](https://twitter.com/thejameskyle) no Twitter.***
* * *
# Noções básicas
Babel is a JavaScript compiler, specifically a source-to-source compiler, often called a "transpiler". This means that you give Babel some JavaScript code, Babel modifies the code, and generates the new code back out.
## ASTs
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Estágios de Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Parse
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntactic Analysis
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transform
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Estado
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Âmbitos
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definições
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Construir o teu primeiro plugin de Babel
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/pt-PT/user-handbook.md
================================================
# Manual do utilizador de Babel
Este documento abrange tudo o que sempre quis saber sobre o uso de [Babel](https://babeljs.io) e relacionadas com o trabalho feito com ferramentas.
[](http://creativecommons.org/licenses/by/4.0/)
Este manual está disponível noutras línguas. Uma lista completa de todas as traduções pode ser encontrada no [README](/README.md).
# Índice
* [Introdução](#toc-introduction)
* [Configurar o Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Introdução
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can use (and create) the next generation of JavaScript, as well as the next generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***Para futuras atualizações, siga [@thejameskyle](https://twitter.com/thejameskyle) no Twitter.***
* * *
# Configurar o Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel o-meu-ficheiro.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel examplo.js --out-file compilado.js
# ou
$ babel examplo.js -o compilado.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***Para futuras atualizações, siga [@thejameskyle](https://twitter.com/thejameskyle) no Twitter.***
================================================
FILE: translations/ro/README.md
================================================
# Manualul Babel
Acest manual este împărţit în două părţi:
* [Manualul utilizatorului](user-handbook.md) - modul de instalare/configurare Babel şi altele.
* [Manualul pentru Plugin-uri](plugin-handbook.md) - cum se pot crea plugin-uri pentru Babel.
> Pentru actualizări, urmăriţi-l pe [@thejameskyle](https://twitter.com/thejameskyle) pe Twitter.
În cazul în care citiți într-o limbă diferită de engleză, este posibil ca unele secțiuni care nu au fost traduse incă, să le găsiți în engleză. Dacă doriți să contribuiți la traducerea acestui document, va trebui să folosiți Crowdin. Vă rugăm să citiți [ghidul de contribuție](/CONTRIBUTING.md) pentru mai multe informații. Veţi găsi cuvinte în limba engleză care sunt conceptele de programare. Dacă acestea ar fi traduse în alte limbi s-ar pierde consistența și fluența în citire. În anumite cazuri veți găsi traducerea literară urmată de termenul în engleză între paranteze `()`. De exemplu: Arbori Abstracți de Sintaxă (ASTs).
================================================
FILE: translations/ro/plugin-handbook.md
================================================
# Manualul pentru Plugin-uri Babel
Acest document descrie cum se pot crea [plugin-uri](https://babeljs.io/docs/advanced/plugins/) pentru [Babel](https://babeljs.io).
[](http://creativecommons.org/licenses/by/4.0/)
Acest manual este disponibil și în alte limbi, a se vedea [README](/README.md) pentru o listă completă.
# Cuprins
* [Introducere](#toc-introduction)
* [Concepte de bază](#toc-basics)
* [ASTs](#toc-asts)
* [Etapele Babel](#toc-stages-of-babel)
* [Analiză](#toc-parse)
* [Analiză Lexicală](#toc-lexical-analysis)
* [Analiză Sintactică](#toc-syntactic-analysis)
* [Transformare](#toc-transform)
* [Generare](#toc-generate)
* [Traversare](#toc-traversal)
* [Vizitatori (Visitors)](#toc-visitors)
* [Trasee (Paths)](#toc-paths)
* [Trasee în Vizitatori (Paths in Visitors)](#toc-paths-in-visitors)
* [Stare](#toc-state)
* [Domenii (Scopes)](#toc-scopes)
* [Legături (Bindings)](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definiții](#toc-definitions)
* [Constructori](#toc-builders)
* [Validatori](#toc-validators)
* [Convertori](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Scrierea primului Plugin Babel](#toc-writing-your-first-babel-plugin)
* [Operații de Transformare](#toc-transformation-operations)
* [Vizitare (Visiting)](#toc-visiting)
* [Aflarea căii unui sub-nod](#toc-get-the-path-of-a-sub-node)
* [Verificare dacă un nod este de un anumit tip](#toc-check-if-a-node-is-a-certain-type)
* [Verificare dacă un nod este de un anumit tip](#toc-check-if-a-path-is-a-certain-type)
* [Verificare dacă un identificator are referință](#toc-check-if-an-identifier-is-referenced)
* [Aflarea căii unui anumit părinte](#toc-find-a-specific-parent-path)
* [Aflarea căilor nodurilor de același nivel](#toc-get-sibling-paths)
* [Oprirea traversării](#toc-stopping-traversal)
* [Manipulare](#toc-manipulation)
* [Înlocuirea unui nod](#toc-replacing-a-node)
* [Înlocuirea unui nod cu mai multe noduri](#toc-replacing-a-node-with-multiple-nodes)
* [Înlocuirea unui nod cu un șir de caractere sursă](#toc-replacing-a-node-with-a-source-string)
* [Inserarea unui nod pe același nivel](#toc-inserting-a-sibling-node)
* [Inserarea într-un containăr](#toc-inserting-into-a-container)
* [Ștergerea unui nod](#toc-removing-a-node)
* [Înlocuirea unui părinte](#toc-replacing-a-parent)
* [Ștergerea unui părinte](#toc-removing-a-parent)
* [Domeniu (Scope)](#toc-scope)
* [Verificare dacă o variabilă locală este legată](#toc-checking-if-a-local-variable-is-bound)
* [Generarea unui UID](#toc-generating-a-uid)
* [Mutarea unei declarații de variabilă într-un domeniu părinte](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Redenumirea unei legături și a referințelor sale](#toc-rename-a-binding-and-its-references)
* [Opțiuni de plugin](#toc-plugin-options)
* [Pre şi Post în plugin-uri](#toc-pre-and-post-in-plugins)
* [Activarea Syntax în plugin-uri](#toc-enabling-syntax-in-plugins)
* [Construirea nodurilor](#toc-building-nodes)
* [Practici preferate](#toc-best-practices)
* [Evitați traversarea AST pe cât posibil](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Îmbinarea vizitatorilor ori de câte ori este posibil](#toc-merge-visitors-whenever-possible)
* [Evitați traversarea când o căutare manuală este suficientă](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizarea vizitatorilor imbricați](#toc-optimizing-nested-visitors)
* [Atenție la structuri imbricate](#toc-being-aware-of-nested-structures)
* [Testarea Unitara](#toc-unit-testing)
# Introducere
Babel este un compilator generic multi-scop pentru JavaScript. Mai mult decât atât, este o colecție de module care pot fi utilizate pentru diverse tipuri de analiză statică.
> Analiza statică este procesul de a analiza cod fără a-l executa. (Analiza de cod, în timp ce se execută este cunoscută sub denumirea de analiză dinamică). Scopul analizei statice variază foarte mult. Poate fi folosită pentru validare (linting), compilare, evidențiere (highlighting), transformare, optimizare, minimizare, și multe altele.
Puteți utiliza Babel pentru a construi diverse tipuri de instrumente care vă pot ajuta să fiți mai productivi și pentru a scrie programe mai bune.
> ***Pentru actualizări, urmăriţi-l pe [@thejameskyle](https://twitter.com/thejameskyle) pe Twitter.***
* * *
# Concepte de bază
Babel este un compilator de JavaScript, mai exact un compilator sursă-la-sursă, deseori numit un "transpiler". Asta înseamnă că dacă îi pasezi cod JavaScript, Babel modifică codul, și generează cod nou.
## ASTs
Fiecare dintre acești pași implică crearea sau lucrul cu un [Arbore Abstract de Sintaxă](https://en.wikipedia.org/wiki/Abstract_syntax_tree) sau AST.
> Babel folosește un AST modificat din [ESTree](https://github.com/estree/estree), cu specificațiile interne aflate [aici](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Examinați [AST Explorer](http://astexplorer.net/) pentru a înțelege mai bine nodurile AST. [Aici](http://astexplorer.net/#/Z1exs6BWMq) este un link, cu exemplul de cod de mai sus.
Același program poate fi reprezentat printr-o listă, ca aceasta:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Sau printr-un obiect JavaScript ca acesta:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Veți observa că fiecare nivel AST are o structură similară:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Notă: Unele proprietăți au fost eliminate pentru simplitate.
Fiecare dintre acestea sunt cunoscute sub denumirea de **Nod**. AST-ul poate fi alcătuit dintr-un singur nod, sute sau mii de noduri. Impreună ele sunt capabile să descrie sintaxa unui program care poate fi folosită pentru analiză statică.
Fiecare Nod are această interfață:
```typescript
interface Node {
type: string;
}
```
Câmpul `type` este un string reprezentând tipul Nodului (ex. `"FunctionDeclaration"`, `"Identifier"`, sau `"BinaryExpression"`). Fiecare tip de Nod definește un set suplimentar de proprietăţi care descriu acel nod.
Există proprietăţi suplimentare pe fiecare Nod, generate de Babel, care descriu poziţia Nodului în codul sursă original.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Aceste proprietăţi `start`, `end`, `loc`, apar în fiecare Nod.
## Etapele Babel
Cele trei etape principale ale Babel sunt **analiză**, **transformare**, **generare**.
### Analiză
Etapa de **analiză**, primeste codul şi produce AST-ul. Există două faze ale analizei în Babel: [**Analiza lexicală**](https://en.wikipedia.org/wiki/Lexical_analysis) şi [**Analiza sintactică**](https://en.wikipedia.org/wiki/Parsing).
#### Analiză Lexicală
Analiza lexicală primeste un şir de cod şi-l transformă într-un flux de simboluri (**tokens**).
Vă puteţi gândi la tokens ca o matrice uni-dimensională de piese de sintaxă a limbii.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Fiecare `type` are un set de proprietăţi care descrie token-ul:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
La fel ca nodurile AST, acestea conțin `start`, `end`, și `loc`.
#### Analiză Sintactică
Analiza sintactică primește un flux de token-uri şi-l transformă într-o reprezentare AST. Folosind informaţiile din token-uri, această fază le va reformata ca un AST care reprezintă structura codului într-un mod care este mai uşor de utilizat.
### Transformare
Etapa de [Transformare](https://en.wikipedia.org/wiki/Program_transformation) primește un AST pe care-l traversează, adăugă, actualizează şi sterge noduri. Această etapă este de departe cea mai complexă din Babel sau din orice alt compilator. Acesta este locul în care plugin-urile acționează de fapt, aşadar va fi subiectul majorității capitolelor din acest manual. Nu vom intra prea adânc în detalii pentru moment.
### Generare
Etapa de [generare de cod](https://en.wikipedia.org/wiki/Code_generation_(compiler)) primește AST-ul final şi-l transformă înapoi într-un şir de cod, creând şi [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Generarea de cod este destul de simplă: se traversează AST-ul și se construiește un şir de caractere care reprezintă codul transformat.
## Traversare
Atunci când doriţi să transformați un AST trebuie să-l [traversați](https://en.wikipedia.org/wiki/Tree_traversal) recursiv.
Să zicem că avem tipul `FunctionDeclaration`. El are câteva proprietăți: `id`, `params` și `body`. Fiecare dintre ele au noduri imbricate.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Vom începe cu `FunctionDeclaration` şi ştim proprietăţile sale interne, astfel încât vom vizita fiecare proprietate şi copiii săi, în ordine.
Apoi vom continua cu `id`, care este un `Identificator`. `Identificatorii` nu au proprietăţi copil, așadar putem merge mai departe.
Urmează `params`, care este o matrice de noduri, așadar le vom vizita pe fiecare în parte. În acest caz este un singur nod care este de asemenea un `Identificator` aşadar putem merge mai departe.
Apoi ajungem la `body`, care este un `BlockStatement` cu o proprietate `body`, care este o serie de noduri, aşa că le vom vizita pe fiecare dintre ele.
Singurul element de aici este un nod `ReturnStatement`, care are un `argument`, vom merge la `argument` unde găsim un `BinaryExpression`.
`BinaryExpression` conține un `operator`, un `left`, şi un `right`. "Operator" nu este un nod, doar o valoare, așadar o ignorăm, şi în schimb vizităm doar `left` şi `right`.
Acest proces de traversare se întâmplă de-a lungul etapei de transformare Babel.
### Vizitatori (Visitors)
Atunci când vorbim despre "a merge" la un nod, ne referim de fapt la a-l **vizita**. Motivul pentru care vom folosi acest termen este pentru că există acest concept de [**vizitator**](https://en.wikipedia.org/wiki/Visitor_pattern).
Vizitatorii sunt un model folosit în traversarea AST, utilizat în diverse limbaje. În termeni simpli, aceștia sunt obiecte cu metode definite pentru a accepta anumite tipuri de nod dintr-un AST. Această definiție poate fi puțin abstractă, așadar să luăm un exemplu.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Notă:** `Identifier() { ... }` este o prescurtare pentru `Identifier: {enter() { ... }}`.
Aceasta este un vizitator simplu care atunci când este utilizat în timpul traversării va apela metoda `Identifier()` pentru fiecare `Identificator` din arbore.
Așadar, cu acest cod, metoda `Identifier()` va fi apelată de patru ori cu fiecare `Identificator` (inclusiv `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
Toate aceste apeluri se petrec la **intrarea** în nod. Cu toate acestea, există, de asemenea, posibilitatea de a apela o metodă vizitator la **ieşire**.
Imaginaţi-vă că avem această structură de arbore:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
În timpul parcurgerii fiecărei ramuri, vom ajunge în cele din urmă într-o înfundătură, unde trebuie să traversăm arborele în sens invers pentru a ajunge la nodul următor. Mergând în jos prin arbore **intrăm** în fiecare nod, iar când parcurgem în sens invers **ieșim** din fiecare nod.
Haideţi să *parcurgem* acest proces de traversare pentru arborele de mai sus.
* Intrare `FunctionDeclaration`
* Intrare `Identifier (id)`
* Înfundătură
* Ieșire `Identifier (id)`
* Intrare `Identifier (params[0])`
* Înfundătură
* Ieșire `Identifier (params[0])`
* Intrare `BlockStatement (body)`
* Intrare `ReturnStatement (body)`
* Intrare `BinaryExpression (argument)`
* Intrare `Identifier (left)`
* Înfundătură
* Ieșire `Identifier (left)`
* Intrare `Identifier (right)`
* Înfundătură
* Ieșire `Identifier (right)`
* Ieșire `BinaryExpression (argument)`
* Ieșire `ReturnStatement (body)`
* Ieșire `BlockStatement (body)`
* Ieșire `FunctionDeclaration`
Așadar, când creaţi un vizitator aveţi două ocazii de a vizita un nod.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
Dacă este necesar, puteţi aplica aceeaşi funcţie mai multor noduri de vizitator prin separare acestora cu o `|` în numele metodei, de exemplu `Identifier|MemberExpression`.
Exemplu de utilizare în plugin-ul [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47)
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
Puteţi deasemenea utiliza un alias ca noduri de vizitator (conform definiției din [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
De exemplu,
`Function` este un alias pentru `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Trasee (Paths)
AST o are în general multe Noduri, dar cum se relaționează ele unul la altul? Am putea avea un singur obiect mutabil gigant, pe care să-l manipulăm şi să avem acces deplin la el, sau putem simplifica acest lucru cu Trasee (**Paths**).
Un Traseu (**Path**) este o reprezentare de obiect a legăturii dintre două noduri.
De exemplu, dacă luăm următorul nod şi copilul său:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
Şi reprezentăm copilul ` Identifier ` ca un Traseu, ar arăta ceva de genul acesta:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
De asemenea, conține metadate suplimentare despre traseu:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
Precum şi foarte multe metode legate de adăugarea, actualizarea, mutarea, şi ștergerea de noduri, dar vom ajunge la ele mai târziu.
Într-un anumit sens, traseele sunt o reprezentare **reactivă** a poziţiei unui nod în arbore şi multe alte informatii despre nod. Ori de câte ori apelați o metodă care modifică arborele, această informaţie este actualizată. Babel gestionează toate acestea pentru a face lucrul cu noduri cât mai ușor posibil.
#### Trasee în Vizitatori (Paths in Visitors)
Când aveţi un vizitator care are o metodă `Identifier()`, de fapt se vizitează traseul, nu nodul. În acest fel se lucrează cu reprezentarea reactivă a nodului, nu cu nodul în sine.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Stare
Starea este duşmanul transformării AST-ului. Starea îți va crea mari probleme şi ipotezele tale despre stare vor fi aproape întotdeauna greşite, din cauza unei sintaxe care nu ai luat-o în considerare.
Să considerăm următorul cod:
```js
function square(n) {
return n * n;
}
```
Să scriem un vizitator rapid, care va redenumi `n` în `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
Acest lucru ar putea funcționa pentru codul de mai sus, dar îl putem strica uşor dacă facem acest lucru:
```js
function square(n) {
return n * n;
}
n;
```
O modalitate mai bună de a rezolva această problema este folosind recursivitate. Așadar, haideți să facem ca într-un film de Christopher Nolan şi să punem un vizitator în interiorul unui vizitator.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Desigur, acesta este un exemplu teoretic, însă demonstrează cum să eliminăm starea globală din vizitatori.
### Domenii (Scopes)
În continuare vom introduce conceptul de [**domeniu**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript utilizează [domeniu lexical](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), care este o structură de arbore, în care fiecare bloc crează un nou domeniu.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Ori de câte ori creaţi o referinţă în JavaScript, fie că este o variabilă, funcție, clasă, parametru, import, etichetî, etc., aceasta aparţine actualului domeniu.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Codul dintr-un domeniu mai adânc poate utiliza o referință dintr-un domeniu superior.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
Un domeniu mai adânc ar putea crea, de asemenea, o referință cu același nume fără a o modifica.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
Când scriem o transformare, vrem să ținem cont de domeniu. Trebuie să ne asigurăm că nu stricăm cod existent în timp ce modificăm diverse părți din el.
Probabil vom dori să adăugăm noi referinţe şi trebuie sa ne asigurăm că acestea nu intră în coliziune cu cele existente. Sau poate vrem doar să găsim unde se referențiază o anumită variabilă. Vrem să fim capabili să urmărim aceste referinţe într-un anumit domeniu.
Un domeniu poate fi reprezentat în felul următor:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
Crearea unui domeniu nou implică pasarea unui traseu şi a unui domeniu părinte. Apoi, în timpul procesului de traversare se colectează toate referințele ("legăturile") din acel domeniu.
Odată ce am făcut acest lucru, există tot felul de metode ce le putem utiliza pe domenii. Însă le vom examina mai târziu.
#### Legături (Bindings)
Toate referinţele aparţin unui anumit domeniu; această relaţie este cunoscută sub denumirea de **legătură**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
O legătură arată astfel:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
Cu aceste informaţii putem găsi toate referințele la o legătură, putem vedea ce tip de legătură este (parametru, declaraţie etc.), putem căuta cărui domeniu îi aparține, sau putem să-i copiem identificatorul. Putem chiar să aflăm dacă este constantă şi, dacă nu, putem afla ce trasee o determină să fie variabilă, nu constantă.
Fiind capabili să spunem dacă o legătură este constantă este utilă pentru multe scopuri, insă cel mai mare este minimizarea codului.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel este de fapt o colecţie de module. În această secţiune vom trece prin cele mai importante, explicând la ce ajută şi cum se utilizează.
> Notă: Acest document nu este un înlocuitor pentru documentaţia detaliată a API-ului, care va fi disponibilă în altă parte în scurt timp.
## [`babylon`](https://github.com/babel/babylon)
Babylon este analizorul din Babel. A început ca o bifurcație din Acorn, este rapid, simplu de utilizat, are o arhitectură bazată pe plugin-uri pentru caracteristici neconvenţionale (precum şi viitoarele standarde).
În primul rând, să-l instalăm.
```sh
$ npm install --save babylon
```
Să începem pur şi simplu prin parsarea unui şir de cod:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
Putem, de asemenea, sa pasăm opţiuni metodei `parse()` astfel:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` poate fi `"module"` sau `"script"`, care este modul în care Babylon ar trebui să-l analizeze. `"module"` va analiza în mod strict (strict mode) şi permite declaraţii de module, `"script"` nu va permite acest lucru si nu va analiza implicit in mod strict.
> **Notă:** `sourceType` va lua valoarea implicită `"script"` si va arunca eroare atunci când găsește valoarea `import` sau `export`. Pasați `sourceType: "module"` pentru a scăpa de aceste erori.
Din moment ce Babylon este construit cu o arhitectură bazată pe plugin-uri, există, de asemenea, o opţiune ` plugins` care permite activarea plugin-urilor interne. Reţineţi că Babylon nu a deschis încă API-ul pentru plugin-uri externe, deşi este posibil sa facă acest lucru în viitor.
Pentru a vedea o listă completă de plugin-uri, a se vedea [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
Modulul de Traversare Babel conține starea generală a arborelui, şi este responsabil pentru înlocuirea, ștergerea şi adăugarea de noduri.
Instalaţi-l prin rularea:
```sh
$ npm install --save babel-traverse
```
Putem să-l folosim alături de Babylon să traversăm şi să actualizăm noduri:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types este o librărie de utilitare, similară cu Lodash, pentru nodurile AST. Conține metode pentru construirea, validarea şi conversia nodurilor AST. Este util pentru curățarea logicii AST cu metode utilitare bine gândite.
Îl puteţi instala prin rularea:
```sh
$ npm install --save babel-types
```
Apoi să-l utilizaţi:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definiții
Babel Types conține definiţii pentru fiecare tip de nod, și informaţii cu privire la ce proprietăţile aparţin cui, ce valori sunt valide, cum se construiește un nod, cum ar trebui traversat nodul şi alias-uri ale nodului.
O definiţie a unui tip de nod arată astfel:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Constructori
Veţi observa mai sus că definiţia pentru `BinaryExpression` are un câmp `builder`.
```js
builder: ["operator", "left", "right"]
```
Acest lucru se datorează faptului că fiecare tip de nod primește o metodă constructor, care, atunci când este utilizată arată în felul următor:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Care creează un AST ca acesta:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Iar atunci când este tipărit arată astfel:
```js
a * b
```
Constructorii, de asemenea, vor valida nodurile pe care le crează şi aruncă erori descriptive dacă sunt folosiți necorespunzător. Ceea ce ne conduce la următorul tip de metodă.
### Validatori
Definiția pentru `BinaryExpression` include informații privind cămpurile (`fields`) nodului şi cum să le validăm.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
Acest lucru este folosit pentru a crea două tipuri de metode de validare. Prima dintre acestea este `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
Aceasta testează pentru a se asigura că nodul este o expresie binară, dar puteţi pasa, de asemenea, un al doilea parametru pentru a se asigura că nodul conţine anumite proprietăţi şi valori.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
Există, de asemenea, mai multe, *ehem*, versiuni dogmatice ale acestor metode, care vor arunca erori în loc sa returneze adevărat (`true`) sau fals (`false`).
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Convertori
> [WIP] în lucru
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator este generatorul de cod pentru Babel. Primește un AST şi îl transformă în cod cu sourcemaps.
Executaţi următoarea comandă pentru a-l instala:
```sh
$ npm install --save babel-generator
```
Apoi folosiți-l
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
Puteţi pasa, de asemenea, opţiuni metodei `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template este un alt modul mic dar incredibil de util. Vă permite să scrie şiruri de cod cu substituenţi care le puteţi folosi în loc de construirea manuală unui AST masiv. În informatică, această capabilitate se numeşte quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Scrierea primului Plugin Babel
Acum că sunteţi familiarizați cu toate elementele de bază din Babel, haideţi să le utilizăm împreună cu API-ul pentru plugin-uri.
Începe cu o `funcţie` care primește obiectul [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) curent.
```js
export default function(babel) {
// plugin contents
}
```
Deoarece îl veţi folosi foarte des, probabil doriți să pasați doar `babel.types` astfel:
```js
export default function({ types: t }) {
// plugin contents
}
```
Apoi, veţi returna un obiect cu o proprietate `visitor` care este principalul vizitator pentru plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Fiecare funcţie în vizitator primește 2 argumente: ` path ` şi ` state `
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Să scriem un plug-in rapid pentru a scoate în evidenţă modul în care funcţionează. Acesta este codul nostru sursă:
```js
foo === bar;
```
Sau în forma AST:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
Vom începe prin adăugarea unei metode vizitator `BinaryExpression`.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Apoi să filtrăm doar token-urile `BinaryExpression` care folosesc operatorul `===`.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Acum să înlocuim proprietatea `left` cu un nou identificator:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
În cazul în care vom rula acest plugin, ar rezulta:
```js
sebmck === bar;
```
Acum să înlocuim si proprietatea `right`.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
Ceea ce conduce la rezultatul nostru final:
```js
sebmck === dork;
```
Super mișto! Primul nostru plugin pentru Babel.
* * *
# Operații de Transformare
## Vizitare (Visiting)
### Aflarea căii unui sub-nod
Pentru a accesa proprietatea unui nod AST în mod normal se accesează nodul şi apoi proprietatea. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
În cazul în care aveţi nevoie să accesați calea proprietății, utilizaţi metoda `get` a traseului, pasându-i numele proprietății.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Verificare dacă un nod este de un anumit tip
Dacă doriţi să verificaţi de ce tip este un anumit nod, modul preferat de a face acest lucru este:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
De asemenea, puteţi face o verificare superficială pentru proprietăţile acelui nod:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
Aceasta este echivalentă cu:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Verificare dacă un nod este de un anumit tip
Un traseu are aceleași metode de verificare a tipului unui nod:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
este echivalent cu a face:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Verificare dacă un identificator are referință
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternativ:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Aflarea căii unui anumit părinte
Uneori va trebui să traversați arborele în sus, până când este îndeplinită o condiţie.
Apelați `callback`-ul cu `NodePath`-ul tuturor părinţii. Când `calback`-ul returnează o valoare adevărată, vom returna acel `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
În cazul în care traseul curent trebuie și el inclus:
```js
path.find((path) => path.isObjectExpression());
```
Găseşte cea mai apropiată funcție sau program părinte:
```js
path.getFunctionParent();
```
Traversați arborele în sus, până când găsim un nod părinte în listă
```js
path.getStatementParent();
```
### Aflarea traseelor nodurilor de același nivel
În cazul în care o cale dintr-o listă, ca în corpul unui ` Function ` / ` Program `, acesta va avea "fraţi".
* Verificaţi dacă un traseu este parte dintr-o listă cu `path.inList`
* Puteţi obţine fraţii din jur cu `path.getSibling(index)`,
* Indicele traseului curent în containerul cu `path.key`,
* Containerului traseului (o serie cu toate traseele nodurilor frați) cu `path.container`
* Obţine numele cheii containerului listei cu `path.listKey`
> Aceste API-uri sunt utilizate în plugin-ul [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) utilizat în [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Oprirea traversării
Dacă plugin-ul dumneavoastră trebuie să-și oprească execuția la un anumit moment, cea mai simplă abordare este să returnați devreme.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
Dacă faceți o sub-traversare într-un traseu de nivel superior, puteţi folosi 2 metode oferite de API:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulare
### Înlocuirea unui nod
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Înlocuirea unui nod cu mai multe noduri
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Notă:** Când se înlocuieşte o expresie cu mai multe noduri, acestea trebuie să fie declaraţii. Acest lucru este necesar deoarece Babel utilizează euristică pe scară largă la înlocuirea nodurilor, ceea ce înseamnă că puteţi face unele transformări destul de complexe, care altfel ar fi extrem de detaliate.
### Înlocuirea unui nod cu un șir de caractere sursă
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Notă:** Nu este recomandat să utilizaţi acest API dacă nu aveți de a face cu șiruri de caractere sursă dinamice, altfel este mult mai eficient pentru a analiza codul în afara vizitatorului.
### Inserarea unui nod pe același nivel
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Notă:** Acesta ar trebui să fie întotdeauna o declaraţie sau o serie de declaraţii. Aceasta utilizează aceleaşi euristici menţionate în [Înlocuirea unui nod cu mai multe noduri](#replacing-a-node-with-multiple-nodes).
### Inserarea într-un container
Dacă doriţi să inseraţi într-o proprietate de nod AST ca asta este un array la fel ca `body`. Este similar cu `insertBefore` / `insertAfter` fiind nevoie doar de a specifica `listKey`, care este de obicei `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Ștergerea unui nod
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Înlocuirea unui părinte
Doar apelați `replaceWith` cu parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Ștergerea unui părinte
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Domeniu (Scope)
### Verificare dacă o variabilă locală este legată
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
Aceasta va parcurge arborele şi va căuta acea legătură anume.
Puteţi verifica și dacă un domeniu are o legătură proprie (**own**):
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generarea unui UID
Următorul cod va genera un identificator fără coliziuni cu nicio variabilă definită local.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Mutarea unei declarații de variabilă într-un domeniu părinte
Uneori, poate doriţi să mutați un `VariableDeclaration`, pentru a-i putea asocia o valoare.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Redenumirea unei legături și a referințelor sale
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternativ, puteţi redenumi o legătură cu un identificator unic generat:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Opțiuni de plugin
Dacă doriţi să lăsați utilizatorii să particularizeze comportamentul plugin-ul vostru Babel, puteţi accepta opţiuni de plugin specifice, pe care utilizatorii le pot specifica în felul următor:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
Aceste opţiuni sunt pasate apoi vizitatorilor plugin-ului prin obiectul `state`:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
Aceste opţiuni sunt specifice plugin-ului şi nu puteţi accesa opţiuni din alte plugin-uri.
## Pre şi Post în plugin-uri
Plugin-urile pot avea funcţii care sunt rulate înainte sau după. Ele pot fi folosite în scopuri de instalare sau curăţare/analiză.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Activarea Syntax în plugin-uri
Plugin-urile pot activa [plugin-uri Babilon](https://github.com/babel/babylon#plugins), astfel încât utilizatorii nu trebuie să le instaleze/activeze. Acest lucru previne erori de interpretare în lipsa moştenirii plugin-ului de sintaxă.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Aruncarea unei Erori de Sintaxă
Dacă doriţi să aruncați o eroare cu babel-code-frame și un mesaj:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
Eroarea arată așa:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Construirea nodurilor
Când scrieţi transformări veţi dori adesea să construiți unele noduri pentru a le insera în AST. Aşa cum am menţionat anterior, puteţi face acest lucru folosind metodele [builder](#builder) din pachetul [`babel-types`](#babel-types).
Numele metodei pentru un constructor este pur şi simplu numele tipului de nod pe care doriţi să-l construiți cu excepţia că prima literă trebuie sa fie mică. De exemplu dacă doriți să construiți `MemberExpression` ar trebui să utilizaţi `t.memberExpression(...)`.
Argumentele acestor constructori sunt stabilite prin definiţia nodului. În momentul de față se lucrează pentru a genera documentaţie uşor de citit pentru definiţii, dar pentru moment toate pot fi găsite [aici](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
O definiţie de nod arată în felul următor:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Aici puteţi vedea toate informaţiile despre acest tip de nod, inclusiv modul de construcție, traversare şi validare.
Uitându-ne la proprietatea `builder`, putem vedea 3 argumente care vor fi necesare pentru a apela metoda constructor (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Reţineţi că, uneori, există mai multe proprietăţi care le puteți particulariza, decât cele conținute în seria constructorului (`builder`). Acest lucru se întâmplă pentru a evita prea multe argumente pe constructor. În aceste cazuri, trebuie să setaţi proprietăţile manual. Un exemplu este [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
Puteţi vedea validarea pentru argumentele constructorului cu obiectul `fields`.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
Puteţi vedea că `object` trebuie să fie `Expression`, `property` trebuie să fie `Expression` sau `Identifier` depinzând dacă expresia membru este calculată (`computed`) sau nu, iar `computed` este pur şi simplu un boolean care implicit este `false`.
Aşadar putem construi un `MemberExpression` în felul următor:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Ceea ce va rezulta în:
```js
object.property
```
Cu toate acestea, am spus că `object` să fie `Expression`, așadar de ce `Identifier` este valid?
Ei bine, dacă ne uităm la definiţia pentru `Identifier` putem vedea că are o proprietate `aliases` care declară că este, de asemenea, o expresie.
```js
aliases: ["Expression", "LVal"],
```
Așadar, din moment ce `MemberExpression` este de tip `Expression`, l-am putea seta ca un `object` pentru alt `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Ceea ce va rezulta în:
```js
member.expression.property
```
Este foarte puţin probabil că veți memora vreodată semnăturile metodei constructor pentru fiecare tip de nod. Așadar, ar trebui să vă rezervați ceva timp să înţelegeți cum sunt generate acestea din definiţiile nodului.
Puteţi găsi toate [definiţiile aici](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) şi le puteţi vedea [documentate aici](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Practici preferate
## Crearea de funcții ajutătoare Constructor şi Verificator
Este destul de simplu pentru a extrage anumite verificări (dacă un nod are un anumit tip) în funcții separate de ajutor, precum şi extragerea de funcții ajutoare pentru tipuri specifice de nod.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Evitați traversarea AST pe cât posibil
Traversarea AST este scumpă, şi este uşor să traversați accidental AST mai mult decât este necesar. Acest lucru ar putea însemna mii daca nu zeci de mii de operaţiuni suplimentare.
Babel optimizează acest lucru cât mai mult posibil, prin îmbinarea vizitatorilor împreună, dacă este posibil, pentru a face totul într-o singură traversare.
### Îmbinarea vizitatorilor ori de câte ori este posibil
Când scrieţi vizitatori, poate fi tentant să apelați `path.traverse` în mai multe locuri unde sunt necesare în mod logic.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
Cu toate acestea, este mult mai bine să scrieți toate acestea ca un vizitator unic care este rulat doar o singură dată. Altfel veți traversa acelaşi arbore mai multe ori pentru niciun motiv.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Evitați traversarea când o căutare manuală este suficientă
De asemenea, poate fi tentant să apelați `path.traverse` atunci când căutați un anumit tip de nod.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
Așadar, în cazul în care căutați ceva specific, este o şansă bună să gasiți nodurile respective printr-o căutare manuală, fără a efectua vreo traversare costisitoare.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizarea vizitatorilor imbricați
Atunci când aveți vizitatori imbricați, ar putea avea mai mult sens să-i scrieți imbricat și în codul dumneavoastră.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
Cu toate acestea, acest lucru creează un nou obiect de vizitator de fiecare dată când este chemat `FunctionDeclaration()`. Acest lucru poate fi costisitor, deoarece Babel face unele procesări de fiecare dată când un obiect vizitator nou este pasat (cum ar fi desfacerea cheilor care conţin mai multe tipuri, validarea şi ajustarea structurii obiectului). Deoarece Babel stochează fanioane pe obiectele vizitator care indică faptul că acesta a efectuat deja această transformare, este mai bine pentru a stoca vizitatorul într-o variabilă şi pasarea aceluiași obiect de fiecare dată.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
Dacă aveţi nevoie de stare în cadrul vizitatorilor imbricați, astfel:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
Puteţi să-l pasați ca stare metodei `traverse()` şi să aveți acces la ea pe obiectul `this` al vizitatorului.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Atenție la structuri imbricate
Uneori când ne gândim la o transformare, am putea uita că structura poate fi imbricată.
De exemplu, imaginaţi-vă că dorim să căutăm `constructor` `ClassMethod` din `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
Putem ușor ignora faptul că clasele pot fi imbricate şi folosind traversarea mai sus ne vom lovi de un `constructor` imbricat, precum:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Testarea Unitara
Există câteva moduri de bază pentru a testa plugin-uri babel: teste de imagine, teste de AST şi teste de execuție. Vom folosi [jest](http://facebook.github.io/jest/) pentru acest exemplu deoarece suportă implicit teste de imagine. Exemplul creat aici este găzduit în [acest repo](https://github.com/brigand/babel-plugin-testing-example).
În primul rând avem nevoie de un plugin de babel, pe care-l vom pune în src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Teste de imagine
Apoi, instalăm dependenţele noastre cu `npm install --save-dev babel-core jest`, şi apoi putem începe scrierea primului nostru test: imaginea. Testele de imagine ne permit să inspectăm vizual rezultatul plug-in-ului babel. Îi pasăm date de intrare, îi spunem să creeze imaginea rezultatului şi să o salveaze într-un fişier. Adăugăm imaginile în git. Acest lucru ne permite să vedem atunci când este afectat rezultatul oricăruia dintre cazurile noastre de testare. Deasemenea, ne arată și diferența dintre cele 2 variante în Pull Requests. Desigur, ai putea face acest lucru cu orice framework de testare, dar cu jest actualizarea imaginilor este foarte ușoară: `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
Codul de mai sus ne creează un fişier de imagine în `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
Dacă vom schimba din 'bar' în 'baz' plugin-ul şi rulăm jest din nou, vom obține acest lucru:
```diff
Valoarea primită nu este identică cu cea salvată 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
Putem vedea cum modificarea noastră în codul de plug-in a afectat rezultatul plugin-ului nostru, iar în cazul în care noul rezultat este ceea ce ne dorim, putem rula `jest -u` pentru a actualiza imaginea salvată.
### Teste AST
În plus faţă de testarea de imagine, putem inspecta manual și AST-ul. Acesta este un exemplu simplu, dar fragil. Pentru situaţii reale, aţi putea folosi babel-traverse. Acest lucru vă permite să specificaţi un obiect cu o cheie de `vizitator`, exact cum utilizaţi pentru plugin-ul în sine.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Teste de Execuție
Aici vom transforma codul şi apoi vom evalua dacă se comportă corect. Reţineţi că nu vom utiliza `assert` în test. Acest lucru asigură faptul că dacă plugin-ul nostru efectuează chestii ciudate, ca de exemplu eliminarea linie assert din greșeală, testul va eșua.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core foloseşte o [abordare similară](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) a testelor de imagine şi de execuție.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
Acest pachet ușurează testarea plugin-urilor. Dacă sunteţi familiarizat cu [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) din ESLint, acest lucru ar trebui să vă fie cunoscut. Puteți urmări [documentația](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) pentru lista completă de posibilități, dar iată un exemplu simplu:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***Pentru actualizări, urmăriţi [@thejameskyle](https://twitter.com/thejameskyle) şi [@babeljs](https://twitter.com/babeljs) pe Twitter.***
================================================
FILE: translations/ro/user-handbook.md
================================================
# Manualul de utilizare Babel
Acest document conține tot ceea ce ați vrut vreodată să ştiți despre utilizarea [Babel](https://babeljs.io) şi a instrumentelor aferente.
[](http://creativecommons.org/licenses/by/4.0/)
Acest manual este disponibil și în alte limbi, a se vedea [README](/README.md) pentru o listă completă.
# Cuprins
* [Introducere](#toc-introduction)
* [Inițializare Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Execuția Babel CLI (Interfața Liniei de Comandă) în cadrul unui proiect](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configurare Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Execuția codului generat de Babel](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configurare Babel (Avansată)](#toc-configuring-babel-advanced)
* [Specificarea manuală a plugin-urilor](#toc-manually-specifying-plugins)
* [Opțiuni de plugin](#toc-plugin-options)
* [Personalizarea Babel în funcție de modul de lucru](#toc-customizing-babel-based-on-environment)
* [Crearea unei presetări](#toc-making-your-own-preset)
* [Babel şi alte instrumente](#toc-babel-and-other-tools)
* [Instrumente de analiză statică](#toc-static-analysis-tools)
* [Verificare cod (Linting)](#toc-linting)
* [Stil de cod](#toc-code-style)
* [Documentație](#toc-documentation)
* [Framework-uri](#toc-frameworks)
* [React](#toc-react)
* [Editoare de text şi IDEs](#toc-text-editors-and-ides)
* [Suport Babel](#toc-babel-support)
* [Forum Babel](#toc-babel-forum)
* [Discuții Babel](#toc-babel-chat)
* [Probleme Babel](#toc-babel-issues)
* [Raportarea unei probleme Babel](#toc-creating-an-awesome-babel-bug-report)
# Introducere
Babel este un compilator generic multi-scop pentru JavaScript. Folosind Babel puteţi utiliza (şi crea) următoarea generaţie de JavaScript, precum şi următoarea generaţie de instrumente JavaScript.
Limbajul JavaScript evoluează în mod constant, iar noile specificații și propuneri vin cu caracteristici noi în mod constant. Babel vă permite să folosiți multe din aceste caracteristici cu mult înainte ca acestea să fie disponibile peste tot.
Babel face acest lucru prin compilarea codului JavaScript scris cu cele mai recente standarde într-o versiune care va funcționa peste tot astăzi. Acest proces este cunoscut sub denumirea de compilare sursă-la-sursă, sau "transpiling".
De exemplu, Babel ar putea transforma sintaxa ES2015 pentru "funcții săgeată", din acest cod:
```js
const square = n => n * n;
```
În acest cod:
```js
const square = function square(n) {
return n * n;
};
```
Însă Babel poate face mult mai mult decât atât, deoarece Babel oferă suport pentru extensii de sintaxă precum JSX pentru React sau Flux pentru verificarea statică a tipurilor.
Mai mult decât atât, totul în Babel este pur şi simplu un plug-in şi oricine își poate crea propriile plugin-uri folosind întreaga putere a Babel-ului, pentru propriile scopuri.
*Chiar mai mult* decât atât, Babel este defalcat într-o serie de module de bază pe care oricine o poate utiliza pentru a construi următoarea generaţie de instrumente JavaScript.
Ecosistemul care a apărut în jurul Babel este masiv și foarte divers. Pe parcursul acestui manual vom acoperi atât instrumentele incluse în Babel, precum şi câteva unelte utile construite de comunitate.
> ***Pentru actualizări, urmăriţi-l pe [@thejameskyle](https://twitter.com/thejameskyle) pe Twitter.***
* * *
# Inițializare Babel
Deoarece în comunitatea JavaScript există multe unelte de build, framework-uri, platforme, etc., Babel are integrări oficiale cu majoritatea dintre acestea. De la Gulp la Browserify, de la Ember la Meteor, cu siguranță există o integrare oficială.
Pe parcursul acestui manual, vom acoperi doar modurile predefinite de inițializare Babel, însă puteţi vizita [pagina de configurare](http://babeljs.io/docs/setup) interactivă pentru toate integrările existente.
> **Notă:** Acest ghid face referire la instrumente de linie de comandă, cum ar fi `node` şi `npm`. Înainte de a continua ar trebui să fiți confortabili cu aceste instrumente.
## `babel-cli`
Babel CLI este un mod simplu de a compila fişiere cu Babel din linia de comandă.
Haideţi să-l instalăm la nivel global pentru a învăţa elementele de bază.
```sh
$ npm install --global babel-cli
```
Putem compila primul nostru fişier astfel:
```sh
$ babel my-file.js
```
Această comandă va afișa codul compilat direct în terminal. Pentru a-l scrie într-un fişier trebuie să specificăm parametrul `--out-file` sau `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
Dacă vrem să compilăm un director întreg într-un director nou, putem face asta folosind `--out-dir` sau `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Execuția Babel CLI (Interfața Liniei de Comandă) în cadrul unui proiect
Deși se *poate* instala Babel CLI și la nivel global pe maşina dvs., este recomandat să-l instalaţi **local**, la nivel de proiect.
Există două motive principale pentru asta.
1. Proiecte diferite pe aceeaşi maşină pot depinde de versiuni diferite de Babel, permiţându-vă actualizarea individuală a lor.
2. Aceasta înseamnă că nu aveţi o dependenţă implicită privind mediul în care lucraţi. Acest lucru face ca proiectul să fie mai portabil și mai ușor de instalat.
Putem instala Babel CLI la nivel local prin rularea:
```sh
$ npm install --save-dev babel-cli
```
> **Notă:** Deoarece, în general, este o idee rea să rulați Babel la nivel global, ar trebui să dezinstalaţi copia globală prin rularea:
>
> ```sh
$ npm uninstall --global babel-cli
```
După ce se termină instalarea, fişierul `package.json` ar trebui să arate așa:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Acum, în loc să rulăm Babel direct din linia de comandă, vom adăuga comenzile noastre în **npm scripts** folosind versiunea noastră locală de Babel.
Pur şi simplu adăugaţi un câmp `"scripts"` în fișierul `package.json` şi adăugați comanda Babel pe proprietatea `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Acum din terminalul nostru se poate rula:
```js
npm run build
```
Această comandă va rula Babel în acelaşi mod, ca înainte, însă folosind o instalare locală.
## `babel-register`
Următoarea metoda comună de rulare Babel este prin `babel-register`. Această opţiune vă va permite să executaţi Babel doar prin cererea fişierelor, ceea ce facilitează integrarea mai bună cu setup-ul vostru.
Reţineţi că acest lucru nu este recomandat pentru utilizarea în producţie. Este considerată practică rea să utilizați cod compilat în acest fel. Este mult mai bine să compilaţi înainte de lansarea codului în producție. Însă acest lucru funcţionează destul de bine pentru script-uri sau alte lucruri pe care le rulați la nivel local.
În primul rând creaţi un fişier `index.js`.
```js
console.log("Hello world!");
```
Dacă am rula acest cod cu `node index.js`, nu ar fi compilat cu Babel. Așadar, vom configura `babel-register`.
Instalați mai întâi `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Apoi, creaţi un fişier `register.js` în proiect şi cu următorul cod:
```js
require("babel-register");
require("./index.js");
```
Acest lucru va *înregistra* Babel în sistemul de module Node şi va începe compilarea fiecărui fişier care este "cerut" cu `require`.
Acum, în loc să rulăm `node index.js` putem folosi `register.js`.
```sh
$ node register.js
```
> **Notă:** Nu puteţi înregistra Babel în acelaşi fişier pe care doriţi să-l compilaţi. Asta deoarece Node va executa fişierul înainte ca Babel să-l compileze.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
Dacă doar rulați cod prin intermediul `node` CLI, cel mai simplu mod de a integra Babel ar fi să utilizaţi `babel-node` CLI, care este în mare parte doar o înlocuire pentru `node` CLI.
Reţineţi că acest lucru nu este recomandat pentru utilizarea în producţie. Este considerată practică rea să utilizați cod compilat în acest fel. Este mult mai bine să compilaţi înainte de lansarea codului în producție. Însă acest lucru funcţionează destul de bine pentru script-uri sau alte lucruri pe care le rulați la nivel local.
În primul rând, asiguraţi-vă că aveţi `babel-cli` instalat.
```sh
$ npm install --save-dev babel-cli
```
> **Notă:** Dacă vă întrebaţi de ce instalăm acest pachet la nivel local, vă rugăm să citiţi secţiunea [Execută Babel CLI (Interfața Liniei de Comandă) în cadrul unui proiect](#toc-running-babel-cli-from-within-a-project) de mai sus.
Apoi ori de câte ori executaţi `node` înlocuiți cu `babel-node`.
Dacă utilizaţi npm `scripts`, puteţi face pur şi simplu:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Altfel va trebui să scrieți calea către `babel-node`.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Sfat: Puteţi utiliza, de asemenea, [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
Dacă aveţi nevoie să utilizaţi Babel programatic, puteţi folosi pachetul `babel-core`.
Mai întâi instalați `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
Dacă aveţi un şir de caractere JavaScript, îl puteţi compila direct folosind `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
Dacă lucraţi cu fişiere, puteţi utiliza fie metoda asincronă:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Fie cea sincronă:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
Dacă aveţi deja un AST Babel, puteți transforma direct din AST.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
Pentru toate metodele de mai sus, `options` se referă la https://babeljs.io/docs/usage/api/#options.
* * *
# Configurare Babel
Poate ați observat până acum că Babel în sine nu pare să facă altceva decât să copieze fişiere JavaScript dintr-o locaţie în alta.
Aceasta se întâmplă deoarece încă nu i-am specificat să facă ceva anume.
> Deoarece Babel este un compilator de uz general, care este utilizat într-o multitudine de moduri diferite, nu face nimic în mod implicit. Trebuie specificat în mod explicit ceea ce ar trebui să facă.
Puteţi configura Babel pentru scopuri specifice prin instalarea de **plugin-uri** sau **presetări** (grupuri de plugin-uri).
## `.babelrc`
Înainte de a începe a-i spune lui Babel ce să facă. Avem nevoie să creăm un fişier de configurare. Tot ce trebuie să facem este să creăm un fişier `.babelrc` la rădăcina proiectului. Să incepem cu următoarele date:
```js
{
"presets": [],
"plugins": []
}
```
Prin intermediul acestui fișier configurăm Babel pentru a face ceea ce dorim.
> **Notă:** În timp ce există și alte metode de setare a opţiunilor Babel, utilizarea fişierul `.babelrc` este cel recomandat.
## `babel-preset-es2015`
Să începem prin a instrui Babel să compileze din ES2015 (cea mai nouă versiune a standardului JavaScript, de asemenea, cunoscut și ca ES6) în ES5 (versiunea disponibilă în cele mai multe medii JavaScript astăzi).
Vom face acest lucru prin instalarea presetării Babel "es2015":
```sh
$ npm install --save-dev babel-preset-es2015
```
Apoi vom modifica fișierul nostru `.babelrc` pentru a include această presetare.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setarea pentru React este la fel de simplă. Doar instalați presetarea:
```sh
$ npm install --save-dev babel-preset-react
```
Apoi adăugați presetarea în fişierul `.babelrc`:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript are, de asemenea, unele propuneri care își urmează drumul lor spre standard, prin procesul TC39 (Comitetul tehnic din spatele standardul ECMAScript).
Acest proces este împărțit în 5 etape (0-4). Când propunerile câştiga tracţiune şi sunt mai susceptibile de a fi acceptate în standard, ele trec prin diferitele etape, în cele din urmă fiind acceptate în standard la etapa 4.
Acestea sunt incluse în Babel ca 4 presetări diferite:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Reţineţi că nu există nici o presetare stage-4, deoarece aceasta este pur şi simplu presetarea `es2015` de mai sus.
Fiecare dintre aceste presetări necesită presetările pentru etapele ulterioare. Adică `babel-preset-stage-1` necesită `babel-preset-stage-2`, care necesită `babel-preset-stage-3`.
Pur şi simplu instalaţi etapa dorită pentru utilizare:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Apoi o puteţi adăuga în configurarea `.babelrc`.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Execuția codului generat de Babel
Am compilat codul cu Babel, însă nu am ajuns la finalul povestirii.
## `babel-polyfill`
Aproape toată sintaxa viitoare al limbajului JavaScript poate fi compilată cu Babel, dar acest lucru nu este valabil și pentru API-uri.
De exemplu, următorul cod conține o funcţie săgeată, care trebuie compilată:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Care se transformă în aceasta:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
Cu toate acestea, acest cod încă nu va funcţiona peste tot deoarece `Array.from` nu există în fiecare mediu de JavaScript.
Uncaught TypeError: Array.from is not a function
Pentru a rezolva această problemă utilizam un așa-numit [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Un polyfill nu este altceva decât o bucata de cod care replică un API nativ, ce nu există în mediul în care rulează. Acest lucru vă permite să utilizaţi API-uri, cum ar fi `Array.from`, înainte de acestea să fie disponibile.
Babel utilizează [core-js](https://github.com/zloirock/core-js) ca polyfill, împreună cu un [regenerator](https://github.com/facebook/regenerator) personalizat pentru buna funcționare a generatoarelor şi funcţiilor asincrone.
Pentru a include polyfill-ul Babel, în primul rând trebuie instalat cu npm:
```sh
$ npm install --save babel-polyfill
```
Apoi, pur şi simplu includeți polyfill-ul în partea de sus a oricărui fişier care are nevoie de el:
```js
import "babel-polyfill";
```
## `babel-runtime`
Pentru a implementa detalii ale specificațiilor ECMAScript, Babel va folosi metode de "ajutor" pentru a păstra codul generat curat.
Deoarece aceste ajutoare pot ajunge destul de lungi, şi fiind adăugate la începutul fiecărui fişier, le puteţi muta într-o singură "instanță", care să fie inclusă.
Începeți prin instalarea pachetelor `babel-plugin-transform-runtime` și `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Apoi actualizaţi `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Acum Babel va compila codul următor:
```js
class Foo {
method() {}
}
```
În acesta:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Altfel ar trebui ca ajutoarele `_classCallCheck` şi `_createClass` să le introducem în fiecare fişier în cazul în care acestea sunt necesare.
* * *
# Configurare Babel (Avansată)
Cei mai mulţi oameni vor utiliza Babel folosind doar presetările sale, însă Babel expune metode mult mai puternice și mai granulate.
## Specificarea manuală a plugin-urilor
Presetările Babel sunt pur şi simplu colecţii de plugin-uri pre-configurate, dacă vrei să faci ceva diferit de specificarea manuală a plugin-urilor. Aceasta funcţionează aproape exact la fel ca presetările.
Instalați mai întâi un plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Apoi adăugaţi câmpul `plugins` în fișierul `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
Acest lucru vă oferă un control mult mai fin asupra transformărilor executate.
Pentru o listă completă de plugin-uri oficiale vizitați [pagina de plugin-uri Babel](http://babeljs.io/docs/plugins/).
De asemenea, aruncați o privire la toate plugin-urile care au fost [construite de către comunitate](https://www.npmjs.com/search?q=babel-plugin). Dacă doriţi să învăţați cum să scrieți propriile plugin-uri, citiți [Manualul pentru Plugin-uri Babel](plugin-handbook.md).
## Opțiuni de plugin
Multe plugin-uri au opţiuni pentru a le configura diferite comportamente. De exemplu, multe transformări au un mod "lejer", care renunță la unele specificații în favoarea unui cod generat mai performant și mai simplu.
Pentru a adăuga opţiuni unui plug-in, faceți următoarea modificare:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> Voi actualiza documentația plugin-urilor pentru a detalia fiecare opţiune, în următoarele săptămâni. [Urmăriţi-mă pentru actualizări](https://twitter.com/thejameskyle).
## Personalizarea Babel în funcție de modul de lucru
Plugin-urile Babel rezolvă multe sarcini diferite. Multe dintre ele sunt instrumente de dezvoltare, care pot ajuta depanarea codului sau integrarea cu diverse alte instrumente. Există, de asemenea, o mulţime de plugin-uri care sunt destinate optimizării codului în producţie.
Din acest motiv este uzuală configurarea Babel în funcție de mediu de lucru. Puteţi face acest lucru cu uşurinţă din fişierul `.babelrc`.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel vă permite configurarea în propritatea `env`, în funcție de mediul curent.
Mediul actual va folosi ` process.env.BABEL_ENV `. Când `BABEL_ENV` nu este disponibil, acesta va recurge la `NODE_ENV`, şi dacă nici acesta nu este disponibil, atunci va lua implicit valoarea `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Notă:** `[COMMAND]` este ceea ce folosiți pentru a rula Babel (adică. `babel`, `babel-node`, sau poate doar `node` dacă utilizaţi babel-register).
>
> **Sfat:** Dacă doriţi funcționarea atât pe platforme Unix cât şi Windows, utilizaţi [`cross-env`](https://www.npmjs.com/package/cross-env).
## Crearea unei presetări
Specificarea manuală a plugin-urilor? Opţiuni ale plugin-urilor? Setãri în funcție de mediu? Toate aceste configurări pot implica multă repetiţie în diferite proiecte.
Din acest motiv este de preferat crearea propriilor presetări. Aceasta ar putea fi o presetare pentru [versiunea Node](https://github.com/leebenson/babel-preset-node5) specifică pe care o utilzați, sau o presetare pentru [întreaga](https://github.com/cloudflare/babel-preset-cf) [companie](https://github.com/airbnb/babel-preset-airbnb).
Pentru a crea o presetare este simplu. Să zicem că avem următorul fișier `.babelrc`:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
Tot ce trebuie să faceţi este să creaţi un nou proiect, urmând convenţia de denumire `babel-preset-*` (vă rugăm să fiți responsabili cu acest spațiu de nume), alături de două fişiere.
În primul rând, creaţi un fişier nou `package.json` cu `dependenţele` presetării voastre.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Apoi creați un fişier `index.js` care exportă conţinutul fişierului `.babelrc`, înlocuind textele din proprietățile plugin și preset cu apeluri `require`.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Apoi publicați-l pe npm şi folosiți-l ca orice altă presetare.
* * *
# Babel şi alte instrumente
Babel este destul de simplu de setat, odată ce te obișnuiesti cu el, dar poate fi destul de dificil să-l integrați cu alte instrumente. Cu toate acestea, încercăm să lucrăm îndeaproape cu alte proiecte pentru a face experiența cât mai plăcută.
## Instrumente de analiză statică
Standardele mai noi aduc o mulţime de sintaxe noi limbajului şi instrumentele de analiză statică doar încep să profite de ele.
### Verificare cod (Linting)
Una dintre cele mai populare instrumente pentru linting este [ESLint](http://eslint.org), din acest motiv întreținem o integrare oficială [`babel-eslint`](https://github.com/babel/babel-eslint).
Pentru început instalați `eslint` şi `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Apoi creați sau folosiți fişierul `.eslintrc` existent în proiectul dumneavoastră şi setaţi `parser-ul` ca `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Acum, adăugaţi o sarcină `lint` în script-urile din `package.json`:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Pe urmă, doar rulaţi sarcina şi instalarea este gata.
```sh
$ npm run lint
```
Pentru mai multe informaţii consultaţi documentaţia [`babel-eslint`](https://github.com/babel/babel-eslint) sau [`eslint`](http://eslint.org).
### Stil de cod
> JSCS a fuzionat cu ESLint, așadar aruncați un ochi peste Code Styling cu ESLint.
JSCS este un instrument extrem de popular care duce linting-ul un pas mai departe în verificarea stilului codului. Responsabilul de bază pentru proiectele Babel şi JSCS ([@hzoo](https://github.com/hzoo)) menține o integrare oficială cu JSCS.
Mai mult de atât, această integrare face parte acum din JSCS sub opțiunea `--exnext`. Așadar integrarea cu Babel este extrem de simplă:
$ jscs . --esnext
Din linia de comandă, sau adăugarea opţiunii `esnext` în fişierul `.jscsrc`.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
Pentru mai multe informaţii consultaţi documentaţia [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) sau [`jscs`](http://jscs.info).
### Documentație
Folosind Babel, ES2015 şi Flux puteți deduce multe despre codul vostru. Folosind [documentation.js](http://documentation.js.org) puteţi genera documentaţii detaliate pentru API-uri foarte ușor.
Documentation.js foloseste Babel în spate pentru a suporta cea mai recentă sintaxă, inclusiv adnotări Flux pentru declararea tipurilor în codul dumneavoastră.
## Framework-uri
Toate framework-urile JavaScript majore sunt axate acum pe alinierea API-uri lor cu viitor limbajului. Din acest motiv, s-a depus un efort considerabil în instrumente.
Framework-urile au posibilitatea nu doar să folosească Babel, ci chiar să-l extindă în moduri care îmbunătățesc experiența utilizatorilor lor.
### React
React şi-a schimbat dramatic API-ul pentru a se alinia cu clasele ES2015 ([Citiți despre actualizarea API-ului aici](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Mai mult de atât, React se bazează pe Babel pentru a compila sintaxa JSX, renunțând la propriul instrument în favoarea Babel. Puteţi începe prin setarea pachetului `babel-preset-react` urmând [instrucţiunile de mai sus](#babel-preset-react).
Comunitatea React a luat Babel şi l-au folosit intens. Există acum o multitudine de transformări [construite de comunitate](https://www.npmjs.com/search?q=babel-plugin+react).
Cel mai notabil ar fi [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) care combinat cu un număr de [transformări specifice React](https://github.com/gaearon/babel-plugin-react-transform#transforms) poate permite lucruri ca *reîncărcarea modulelor* şi alte utilităţi de depanare.
## Editoare de text şi IDEs
Introducerea sintaxei ES2015, JSX şi Flux cu Babel poate fi de ajutor, dar dacă editorul de text nu are suport pentru acestea atunci poate fi o experienţă neplacută. Pentru acest motiv, veţi dori să vă configurați editorul de text sau IDE-ul cu un plugin Babel.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Suport Babel
Babel are o comunitate foarte mare şi în plină creştere, iar odată cu dezvoltarea noastră vrem să ne asigurăm că oamenii au toate resursele de care au nevoie pentru a avea succes. Așadar, oferim mai multe metode pentru a obţine sprijin si ajutor.
Amintiţi-vă că în toate aceste comunităţi, se aplică un [Cod de Conduită](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). Dacă nu se respectă codul de conduită, vor fi luate măsuri. Așadar, vă rugăm să-l citiţi cu atenție şi sa țineți cont de el atunci când interacţionați cu ceilalţi.
Căutăm, de asemenea, să creștem o comunitate auto-susţinută, pentru persoanele care stau prin preajmă şi îi sprijină pe alţii. Dacă cineva pune o întrebare si cunoasteți răspunsul, răpiți-vă câteva minute și dați-le o mână de ajutor. Încercați să fiți blând şi înţelegător atunci când faceți acest lucru.
## Forum Babel
[Discourse](http://www.discourse.org) ne găzduiește gratuit o versiune a forum-ului lor (şi noi îi iubim pentru aceasta!). În cazul în care preferați forumurile, faceți o vizită la [discuss.babeljs.io](https://discuss.babeljs.io).
## Discuții Babel
Toată lumea iubeşte [Slack](https://slack.com). Dacă sunteţi în căutare pentru asistenţă imediată din partea comunităţii, intrați pe [slack.babeljs.io](https://slack.babeljs.io).
## Probleme Babel
Babel utilizează issue tracker-ul oferit de [Github](http://github.com).
Puteţi vedea toate problemele existente şi rezolvate pe [Github](https://github.com/babel/babel/issues).
Dacă doriţi să raportați o nouă problemă:
* [Căutați dacă nu cumva a fost creată de altcineva înainte](https://github.com/babel/babel/issues)
* [Raportați un bug](https://github.com/babel/babel/issues/new) sau [solicitați o funcționalitate nouă](https://github.com/babel/babel/issues/new)
### Raportarea unei probleme Babel
Problemele Babel pot fi uneori foarte dificil de depanat la distanţă, aşa că avem nevoie de tot ajutorul posibil. Petrecerea câtorva minute în plus pentru a crea un raport frumos și util pot ajuta în rezolvarea mult mai rapidă a problemei.
În primul rând, încercaţi izolarea problemei. Este extrem de puţin probabil ca fiecare parte a setup-ul să contribuie la această problemă. În cazul în care problema este o bucată de cod de intrare, încercaţi să ştergeţi codul cât mai mult posibil care cauzează problema.
> [WIP] în lucru
* * *
> ***Pentru actualizări, urmăriţi-l pe [@thejameskyle](https://twitter.com/thejameskyle) pe Twitter.***
================================================
FILE: translations/ru/README.md
================================================
# Справочник Babel
Это руководство разделено на две части:
* [Руководство Пользователя](user-handbook.md) - Установка и настройка Babel.
* [Руководство Плагинов](plugin-handbook.md) - Создание плагинов для Babel.
> Оставайтесь в курсе последних обовлений - подписывайтесь в Твиттере на [@thejameskyle](https://twitter.com/thejameskyle).
Если вы читаете это не на английском языке, то вы можете наткнуться на разделы на английском, которые ещё не переведены. Если вы хотите помочь с переводом, то вы должны делать это, используя Crowdin. Прочтите, пожалуйста, [рекомендации](/CONTRIBUTING.md) для подробной информации. Также Вы можете встретить слова на английском, которые являются понятиями программирования. Они не были переведены умышленно, дабы сохранить удобство чтения и единство стиля. Хотя во многих случаях Вы найдете их буквальный перевод, а затем и их оригинал в скобках `()`. Например: Абстрактные Синтаксические Деревья (ASTs).
================================================
FILE: translations/ru/plugin-handbook.md
================================================
# Руководство Плагинов Babel
В этом документе описано как создавать [плагины](https://babeljs.io/docs/advanced/plugins/) для [Babel](https://babeljs.io).
[](http://creativecommons.org/licenses/by/4.0/)
Это руководство также доступно и на других языках, см. [файл README](/README.md) для получения полного списка.
# Содержание
* [Введение](#toc-introduction)
* [Базовые концепции](#toc-basics)
* [Абстрактные синтаксические деревья (ASTs)](#toc-asts)
* [Этапы работы Babel](#toc-stages-of-babel)
* [Парсинг](#toc-parse)
* [Лексический анализ](#toc-lexical-analysis)
* [Синтаксический анализ](#toc-syntactic-analysis)
* [Трансформация](#toc-transform)
* [Генерация](#toc-generate)
* [Обход](#toc-traversal)
* [Посетители](#toc-visitors)
* [Пути](#toc-paths)
* [Пути в Посетителях](#toc-paths-in-visitors)
* [Состояние](#toc-state)
* [Области видимости](#toc-scopes)
* [Привязка контекста](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Определения](#toc-definitions)
* [Строители](#toc-builders)
* [Валидаторы](#toc-validators)
* [Преобразователи](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Создание вашего первого плагина Babel](#toc-writing-your-first-babel-plugin)
* [Операции преобразования](#toc-transformation-operations)
* [Посещение](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Манипуляция](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Область видимости](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Параметры плагина](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Построение узлов](#toc-building-nodes)
* [Лучшие практики](#toc-best-practices)
* [Избегайте обхода AST насколько это возможно](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Слияние посетителей, когда это возможно](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Оптимизации вложенных посетителей](#toc-optimizing-nested-visitors)
* [Избегайте вложенных структур](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Введение
Babel - это многоцелевой компилятор общего назначения для JavaScript. Более того, это коллекция модулей, которая может быть использована для множества различных форм синтаксического анализа.
> Статический анализ - это процесс анализа кода без запуска этого кода. (Анализ кода во время выполнения известен как динамический анализ). Цели синтаксического анализа очень разнообразны. Это может быть использовано для контроля качества кода (linting), компиляции, подсветки синтаксиса, трансформации, оптимизации, минификации и много другого.
Вы можете использовать Babel для создания множества различных инструментов, которые помогут Вам стать более продуктивным и писать лучшие программы.
> ***Оставайтесь в курсе последних обовлений - подписывайтесь в Твиттере на [@thejameskyle](https://twitter.com/thejameskyle).***
* * *
# Базовые концепции
Babel - это JavaScript компилятор, точнее компилятор, преобразующий программу на одном языке в программу на другом языке (source-to-source compiler), часто называемый трянслятор. Это означает, что вы даёте Babel некоторый JavaScript код, а Babel модифицирует его, генерирует новый код и возвращает его.
## Абстрактные синтаксические деревья (ASTs)
Каждый из этих шагов требует создания или работы с [Абстрактным синтаксическим деревом](https://en.wikipedia.org/wiki/Abstract_syntax_tree), или AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Взгляните на [AST Explorer](http://astexplorer.net/) чтобы получить более полное представление об AST-нодах. [Здесь](http://astexplorer.net/#/Z1exs6BWMq) находится ссылка на него с уже скопированным примером выше.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Или в виде JavaScript-объекта, вроде этого:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Вы могли заметить, что каждый уровень AST имеет одинаковую структуру:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Некоторые свойства были убраны для упрощения.
Каждый из этих уровней называется **Нода (Node)**. Отдельный AST может состоять как из одной ноды, так и из сотен, если не тысяч нод. Все вместе они способны описать синтаксис программы, который может быть использован для статического анализа.
Каждая нода имеет следующий интерфейс:
```typescript
interface Node {
type: string;
}
```
Поле `type` - это строка, описывающая, чем является объект, представляемый данной нодой (т.е. `"FunctionDeclaration"`, `"Identifier"`, или `"BinaryExpression"`). Каждый тип ноды определяет некоторый дополнительный набор полей, описывающий этот конкретный тип.
Пример. Каждая нода, сгенерированная Babel, имеет дополнительные свойства, которые описывают позицию этой ноды в оригинальном исходном коде.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Эти свойства - `start`, `end`, `loc` - присутствуют в каждой отдельной ноде.
## Этапы работы Babel
Три основных этапа работы Babel это **парсинг**, **трансформация**, **генерация**.
### Парсинг
Стадия **разбора** принимает код и выводит AST. Существуют два этапа разбора в Babel: [**Лексический анализ**](https://en.wikipedia.org/wiki/Lexical_analysis) и [**Синтаксический анализ**](https://en.wikipedia.org/wiki/Parsing).
#### Лексический анализ
Лексический анализ будет принимать строку кода и превращать его в поток **токенов**.
Вы можете думать о токенах как о плоском массиве элементов синтаксиса языка.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Каждый `тип` здесь имеет набор свойств, описывающих токен:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Как узлы AST, они также имеют `start`, `end` и `loc`.
#### Синтаксический анализ
Синтаксический анализ примет поток токенов и преобразует их в AST представление. Используя информацию в токенах, этот этап переформатирует их как AST, который отображает структуру кода таким образом, что облегчает работу с ним.
### Трансформация
Этап преобразования принимает AST и проходит через него, добавляя, обновляя, и удаляя узлы по мере прохождения. Это, безусловно, наиболее сложная часть Babel или любого компилятора. Здесь работают плагины и это будет предметом обсуждения большей части этого руководства. Поэтому мы не погружаемся слишком глубоко прямо сейчас.
### Генерация
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Генерация кода довольно проста: вы проходите через AST в глубину, строя строку, которая представляет преобразованный код.
## Обход
Когда вы хотите трансорфмировать AST вам необходимо [пройти по всему дереву](https://en.wikipedia.org/wiki/Tree_traversal) рекурсивно.
Скажем, у нас есть тип `FunctionDeclaration`. Он имеет несколько свойств: `id`, `params` и `body`. Каждый из них имеет вложенные узлы.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Итак, мы начинаем с `FunctionDeclaration`, и мы знаем его внутренние свойства, поэтому мы посещаем каждое из них и их детей в по порядку.
Далее мы идем к `id`, который представляет собой ` Identifier `. ` Identifier`ы не имеют дочерних узлов, поэтому мы двигаемся дальше.
Затем следует `params`, который представляет собой массив узлов, и мы посещаем каждый из них. В данном случае это один узел, который также является ` Identifier`. Идем дальше.
Затем мы попали `тело`, которое является `BlockStatement` со свойством `body`, которое является массивом узлов, поэтому мы проходимся по каждому из них.
Единственным элементом здесь является узел `ReturnStatement`, который имеет `argument`, мы идем на `argument` и находим выражение `BinaryExpression`.
`BinaryExpression` имеет `оператор`, `левую часть` и `правую часть`. Оператор это не узел, а просто значение, поэтому мы не переходим к нему, и вместо этого просто посещаем `левую` и `правую` части.
Этот процесс обхода происходит в Babel на этапе преобразования.
### Посетители
Когда мы говорим о том чтобы «пройти» к узлу, мы на самом деле имеем ввиду что мы **посещаем** его. Причина, по которой мы используем этот термин, потому что есть эта концепция [**посетителя**](https://en.wikipedia.org/wiki/Visitor_pattern).
Посетители – шаблон, используемый в AST для обхода различных языков. Simply put they are an object with methods defined for accepting particular node types in a tree. Это немного абстрактно, поэтому давайте рассмотрим пример.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Примечание:** `Identifier() { ... }` является краткой формой для `идентификатор: {enter() { ... }}`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
Так с этим кодом `Identifier()` метод будет вызываться в четыре раза с каждым `Identifier` (включая `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
Все эти вызовы на узле, **enter**. Однако существует также возможность вызова метода посетителя на **exit**.
Представьте, что у нас есть следующая древовидная структура:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
Как мы проходим вниз по каждой ветви дерева мы в конечном итоге доходим до тупика, где мы должны пройти обратно вверх по дереву, чтобы попасть на следующий узел. Идя вниз по дереву мы **enter** в каждый узел, затем возвращаясь мы **exit** из каждого узла.
Давайте *пройдем* через этот процесс для дерева из примера выше.
* Вход в `FunctionDeclaration`
* Вход в `Identifier (id)`
* Попадание в тупик
* Выход из `Identifier (id)`
* Вход в `Identifier (params[0])`
* Попадание в тупик
* Выход из `Identifier (params[0])`
* Вход в `BlockStatement (body)`
* Вход в `ReturnStatement (body)`
* Вход в `BinaryExpression (argument)`
* Вход в `Identifier (left)`
* Попадание в тупик
* Выход из `Identifier (left)`
* Вход в `Identifier (right)`
* Попадание в тупик
* Выход из `Identifier (right)`
* Выход из `BinaryExpression (argument)`
* Выход из `ReturnStatement (body)`
* Выход из `BlockStatement (body)`
* Выход из `FunctionDeclaration`
Итак, при создании посетителей у вас есть две возможности для посещения узла.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Пути
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Пути в Посетителях
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Состояние
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Области видимости
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Привязка контекста
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Примечание: это не замена для подробной API документации, которая вскоре будет доступна где-то в другом месте.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Примечание:** значением `sourceType` по умолчанию является `"script"`, и если будет найден ` import ` или ` export `, то произойдет ошибка. Передайте `sourceType: "module"` чтобы избежать этой ошибки.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Определения
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Строители
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Валидаторы
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Преобразователи
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Создание вашего первого плагина Babel
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Операции преобразования
## Посещение
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Манипуляция
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Область видимости
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Параметры плагина
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Построение узлов
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Лучшие практики
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Избегайте обхода AST насколько это возможно
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Слияние посетителей, когда это возможно
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Оптимизации вложенных посетителей
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Избегайте вложенных структур
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/ru/user-handbook.md
================================================
# Babel. Руководство Пользователя
Этот документ охватывает все, что Вы когда-либо хотели знать об использовании [Babel](https://babeljs.io) и его инструментах.
[](http://creativecommons.org/licenses/by/4.0/)
Это руководство также доступно и на других языках, см. [файл README](/README.md) для получения полного списка.
# Содержание
* [Введение](#toc-introduction)
* [Настройка Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Запуск Babel CLI внутри проекта](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Настройка Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Запуск кода, который сконструирован в Babel](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Настройка Babel (Продвинутый уровень)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel и другие инструменты](#toc-babel-and-other-tools)
* [Инструменты статического анализа](#toc-static-analysis-tools)
* [Линтинг (инструменты для проверки кода)](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Поддержка Babel](#toc-babel-support)
* [Babel форум](#toc-babel-forum)
* [Babel чат](#toc-babel-chat)
* [Вопросы о Babel](#toc-babel-issues)
* [Как создать полезный баг-репорт по Babel](#toc-creating-an-awesome-babel-bug-report)
# Введение
Babel — это универсальный многоцелевой компилятор для JavaScript. С его помощью можно использовать и создавать следующее поколение JavaScript, а также следующее поколения инструментов JavaScript.
JavaScript постоянно развивается, разрастается новыми спецификациями и предложениями, новыми функции появляются буквально каждый день. Использование Babel позволит Вам реализовать многие из этих особенностей намного лет вперед, другими словами заглянуть в будущее.
Babel делает это путем компиляции JavaScript кода, написанного по новейшим стандартам в ту версию, которая будет работать на текущей реализации. Этот процесс известен как компиляция кода.
Например, Babel может преобразовать код, написанный по новому синтаксису реализации ES2015:
```js
const square = n => n * n;
```
Вот в такой:
```js
const square = function square(n) {
return n * n;
};
```
Тем не менее, Babel может сделать гораздо больше, поскольку он имеет поддержку для синтаксиса, например React'a или Flow.
Более того, Babel - это всего лишь плагин, и любой желающий может его использовать и создавать свои собственные плагины, используя всю мощь Babel, тем самым улучшая его.
Более того, Babel разбит на ряд основных модулей, которые любой желающий сможет использовать для создания следующего поколения инструментов JavaScript.
Многие уже делают так, расширяя и без того разнообразную "экосистему" инструментов, основанных на Babel. С помощью этого руководства я постараюсь объяснить, как работают встроенные инструменты Babel и поделиться некоторыми полезными замечаниями от сообщества.
> ***Оставайтесь в курсе последних обовлений - подписывайтесь в Твиттере на [@thejameskyle](https://twitter.com/thejameskyle).***
* * *
# Настройка Babel
Так как javascript-сообщество имеет не один сборщик пакетов, фреймворк, платформу и т. д., Babel имеет официальную интеграцию для всех основных инструментов. Все, от Gulp до Browserify, от Ember до Meteor, не важно каковы ваши предпочтения, - скорее всего для них найдется официальная интеграция.
В рамках целей данного руководства мы охватим только встроенные пути установки Babel, но вы также можете посетить интерактивную [страницу установки](http://babeljs.io/docs/setup) для выбора необходимых интеграций.
> **Примечание:** Это руководство будет ссылаться на инструменты командной строки такие как `node` и `npm`. Перед продолжением чтения вам следует почувствовать себя комфортно с этими инструментами.
## `babel-cli`
Интерфейс коммандной строки (Command Line Interface, CLI) Babel это простой путь для компилляции файлов с помощью Babel из коммандной строки.
Давайте вначале установим его глобально, чтобы изучить основы.
```sh
$ npm install --global babel-cli
```
Мы можем скомпиллировать наш первый файл как показано дальше:
```sh
$ babel my-file.js
```
Это выведет скомпилированный результат прямо в ваш терминал. Для записи в файл следует использовать `--out-file` или `-o`.
```sh
$ babel example.js --out-file compiled.js
# или
$ babel example.js -o compiled.js
```
Если мы хотим скомпиллировать целую директорию в новую директорию, мы можем это сделать используя `--out-dir` или `-d`.
```sh
$ babel src --out-dir lib
# или
$ babel src -d lib
```
### Запуск Babel CLI внутри проекта
Итак, вы *можете* установить Babel CLI глобально на вашей машине, но будет гораздо лучше установить его **локально** для каждого проекта.
Есть две основные причины для этого.
1. Разные проекты на одной машине могут основываться на разных версиях Babel, позволяя вам обновлять их только по отдельности.
2. Это значит, что вы не будете иметь неявных зависимостей от окружения, в котором работаете и сделаете свой проект гораздо более переносимым и легким для установки.
Мы можем установить Babel CLI локально, запустив:
```sh
$ npm install --save-dev babel-cli
```
> **Примечание:** Так как запускать Babel глобально мы будем считать плохой идеей, для деинсталляции глобальной копии Babel вы можете использовать:
>
> ```sh
$ npm uninstall --global babel-cli
```
После завершения установки, ваш файл `package.json` должен выглядеть примерно так:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Теперь вместо запуска Babel непосредственно из командной строки мы встроим наши команды в **npm scripts**, который будет использовать нашу локальную версию.
Просто добавьте поле `"scripts"` в ваш `package.json` и вставьте команду `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Теперь из нашего терминала мы можем запустить:
```js
npm run build
```
Это запустит Babel так же, как раньше, но в виде локальной копии.
## `babel-register`
Следующий наиболее общий способ запуска Babel используя `babel-register`. Эта опция позволит вам запускать Babel просто запрашивая необходимые файлы, которые могут упростить интеграцию.
Заметим, что это не самый подходящий вариант для продакшена. Не рекомендуется использовать код, скомпилированный подобным способом. Гораздо эффективнее скомпилировать код заранее. Тем не менее, этот вариант может подойти для сборки скриптов и других действий, которые вы захотите проделать на локальной машине.
Для начала добавим в наш проект файл `index.js`.
```js
console.log("Hello world!");
```
Если мы теперь запустим команду `node index.js`, файл не будет скомпилирован с помощью Babel. Так что вместо этого, мы установим `babel-register`.
Устанавливаем `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Затем, в проекте создаем файл `register.js` и пишем следующий код:
```js
require("babel-register");
require("./index.js");
```
Таким образом мы *регистрируем* Babel в системе модулей Node и начинаем компиляцию всех файлов, добавленных в `require`.
Теперь вместо команды `node index.js` мы можем использовать `register.js`.
```sh
$ node register.js
```
> **Примечание:** Вы не сможете зарегистрировать Babel в том же файле, который собираетесь компилировать, поскольку node исполняет этот файл перед тем, как Babel доходит до его компиляции.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
Если вы просто запускаете некоторый код посредством `node` CLI (интерфейса командной строки), то самым легким способом подключить Babel станет, пожалуй, `babel-node` CLI, являющийся, по сути, всего лишь заменой `node` CLI.
Заметим, что это не самый подходящий вариант для продакшена. Не рекомендуется использовать код, скомпилированный подобным способом. Гораздо эффективнее скомпилировать код заранее. Тем не менее, этот вариант может подойти для сборки скриптов и других действий, которые вы захотите проделать на локальной машине.
Для начала, убедитесь, что у вас установлен `babel-cli`.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Затем, переключитесь на работу с `babel-node` везде, где вы работаете с `node`.
Если вы используете npm `scripts`, вы можете сделать так:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Либо вам придется прописать путь к собственно `babel-node`.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Подсказка: Вы также можете использовать [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
Если вы хотите использовать Babel программно, то подключите пакет `babel-core`.
Установите `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
Если вам нужно скомпилировать строку JavaScript кода, можно воспользоваться `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
Если вы работаете с файлами, вы можете использовать асинхронные api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Либо синхронные api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
Если у вас уже, по какой-то причине, есть Babel AST, вы можете преобразовывать непосредственно AST.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Настройка Babel
Как вы могли заметить, сам по себе Babel при запуске только копирует Javascript файлы из одного места в другое.
Это происходит потому что мы еще не сказали Babel что следует делать.
> Так как Babel представляет собой компилятор общего назначения, который можно использовать множеством способов, то по умолчанию он не делает ничего. Необходимо явно указать Babel, что ему следует делать.
Вы можете снабдить Babel необходимыми инструкциями, установив **плагины** либо **пресеты** (группы плагинов).
## `.babelrc`
Перед тем как мы начнем говорить Babel, что ему следует сделать, мы создадим файл конфигурации. Для этого нужно всего лишь создать файл `.babelrc` в корне вашего проекта. Запишите в него следующее:
```js
{
"presets": [],
"plugins": []
}
```
Используйте этот файл, чтобы настроить Babel свои специфические требования.
> **Примечание:** Хотя вы можете настроить Babel и другими путями, файл `.babelrc` является лучшим и общепризанным стандартным способом это сделать.
## `babel-preset-es2015`
Начнем с того, что укажем Babel компилировать ES2015 ( современную версию стандарта JavaScript, также известную как ES6) в ES5 ( наиболее доступную на сегодня версию в большинстве средах JavaScript).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Запуск кода, который сконструирован в Babel
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Настройка Babel (Продвинутый уровень)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel и другие инструменты
Babel довольно прост в установке, когда вы только начинаете знакомство с ним, однако настройка его работы с прочими инструментами может оказаться непростой задачей. Тем не менее мы тесно сотрудничаем с другими проектами, чтобы как можно сильнее упростить этот процесс.
## Инструменты для статического анализа
Новейшие стандарты добавляют в язык новый синтаксис и инструменты статического анализа только начинают пользоваться этими преимуществами.
### Линтинг (инструменты для проверки кода)
Один из самых популярных инструментов для линтинга (проверки кода) - это [ESLint](http://eslint.org), по этой причине мы поддерживаем официальную интеграцию [`babel-eslint`](https://github.com/babel/babel-eslint).
Сначала установите `eslint` и `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Затем создайте или используйте существующий файл `.eslintrc` в вашем проекте и установите `parser` как `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Теперь добавьте `lint`-задачу в ваш npm `package.json` скрипт:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Затем просто запустите данную задачу, - вы все настроили.
```sh
$ npm run lint
```
За дальнейшей информацией обращайтесь к документации [`babel-eslint`](https://github.com/babel/babel-eslint) или [`eslint`](http://eslint.org).
### Стиль кода
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Поддержка Babel
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel форум
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel чат
Все любят [Slack](https://slack.com). Если вы ищите оперативную помощь со стороны сообщества, тогда заходите к нам в чат [slack.babeljs.io](https://slack.babeljs.io)..
## Вопросы о Babel
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Как создать полезный баг-репорт по Babel
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***Оставайтесь в курсе последних обовлений - подписывайтесь в Твиттере на [@thejameskyle](https://twitter.com/thejameskyle).***
================================================
FILE: translations/sk-SK/README.md
================================================
# Babel príručka
Príručka je rozdelená do dvoch častí:
* [Užívateľská príručka](user-handbook.md) - Ako nainštalovať/nakonfigurovať Babel a ďalšie.
* [Príručka pluginov (rozšírení)](plugin-handbook.md) - Ako vytvárať pluginy pre Babel.
> Aktualizácie sledujte na Twitteri [@thejameskyle](https://twitter.com/thejameskyle).
Ak čítaš neanglický preklad tejto príručky, stále možeš nájsť anglické časti, ktoré zatiaľ neboli preložené. Ak by si chcel/a prispieť k jednému z prekladov, musíš to urobiť cez Crowdin. Pre viac informácií si prosím prečítaj [pravidlá prispievania](/CONTRIBUTING.md). Niektoré anglické výrazy vyjadrujú programovacie koncepty. Ak by sa tieto výrazy preložili do iných jazykov, vznikla by nekonzistencia a čítanie o nich by stratilo plynulosť. V mnoho prípadoch nájdeš doslovný preklad, za ktorým v zátvorkách `()` nasleduje anglický výraz. Napríklad: Abstraktné syntaktické stromy (AST).
================================================
FILE: translations/sk-SK/plugin-handbook.md
================================================
# Príručka Babel pluginov
Tento dokument popisuje, ako vytvoriť [Babel](https://babeljs.io) [pluginy](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
Táto príručka je k dispozícii v iných jazykoch, pozri [README](/README.md) pre úplný zoznam.
# Obsah
* [Úvod](#toc-introduction)
* [Základy](#toc-basics)
* [Abstraktné syntaktické stromy (AST)](#toc-asts)
* [Etapy Babelu](#toc-stages-of-babel)
* [Analýza](#toc-parse)
* [Lexikálna analýza](#toc-lexical-analysis)
* [Syntaktická analýza](#toc-syntactic-analysis)
* [Transformácia](#toc-transform)
* [Generovanie](#toc-generate)
* [Prechod (Traversal)](#toc-traversal)
* [Inšpektori (Visitors)](#toc-visitors)
* [Cesty](#toc-paths)
* [Cesty v inšpektoroch](#toc-paths-in-visitors)
* [Stav](#toc-state)
* [Rozsahy (Scopes)](#toc-scopes)
* [Väzby](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definície](#toc-definitions)
* [Stavitelia (Builders)](#toc-builders)
* [Validátory](#toc-validators)
* [Konvertory](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Písanie tvojho prvého Babel pluginu](#toc-writing-your-first-babel-plugin)
* [Transformačné operácie](#toc-transformation-operations)
* [Inšpekcie (Visiting)](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulácia](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Rozsah (Scope)](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Možnosti pluginov](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Stavba uzlov](#toc-building-nodes)
* [Osvedčené postupy](#toc-best-practices)
* [Vyhni sa čo najviac prechádzaniu AST](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Zlučuj inšpektory kedykoľvek je to možné](#toc-merge-visitors-whenever-possible)
* [Neprechádzaj stromom, ak to zvládne manuálne prehľadávanie](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimalizácia vnorených návštevníkov](#toc-optimizing-nested-visitors)
* [Uvedom si vnorené štruktúry](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Úvod
Babel je všeobecný viacúčelový kompilátor pre JavaScript. Naviac je to kolekcia modulov, ktoré môžu byť použité pre mnoho rôznych foriem statickej analýzy.
> Static analysis is the process of analyzing code without executing it. (Analysis of code while executing it is known as dynamic analysis). The purpose of static analysis varies greatly. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
You can use Babel to build many different types of tools that can help you be more productive and write better programs.
> ***Aktualizácie sledujte na Twitteri [@thejameskyle](https://twitter.com/thejameskyle).***
* * *
# Základy
Babel is a JavaScript compiler, specifically a source-to-source compiler, often called a "transpiler". This means that you give Babel some JavaScript code, Babel modifies the code, and generates the new code back out.
## Abstraktné syntaktické stromy (AST)
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Etapy Babelu
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Analýza
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexikálna analýza
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntaktická analýza
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transformácia
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generovanie
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Prechod (Traversal)
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Inšpektori (Visitors)
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Cesty
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Cesty v inšpektoroch
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Stav
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Rozsahy (Scopes)
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Väzby
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definície
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Stavitelia (Builders)
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validátory
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Konvertory
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Písanie tvojho prvého Babel pluginu
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformačné operácie
## Inšpekcie (Visiting)
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulácia
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Rozsah (Scope)
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Možnosti pluginov
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Stavba uzlov
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Osvedčené postupy
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Vyhni sa čo najviac prechádzaniu AST
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Zlučuj inšpektory kedykoľvek je to možné
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Neprechádzaj stromom, ak to zvládne manuálne prehľadávanie
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimalizácia vnorených návštevníkov
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Uvedom si vnorené štruktúry
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/sk-SK/user-handbook.md
================================================
# Babel užívateľská príručka
This document covers everything you ever wanted to know about using [Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
Táto príručka je k dispozícii v iných jazykoch, pozri [README](/README.md) pre úplný zoznam.
# Obsah
* [Úvod](#toc-introduction)
* [Setting up Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Úvod
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can use (and create) the next generation of JavaScript, as well as the next generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***Aktualizácie sledujte na Twitteri [@thejameskyle](https://twitter.com/thejameskyle).***
* * *
# Setting up Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***Aktualizácie sledujte na Twitteri [@thejameskyle](https://twitter.com/thejameskyle).***
================================================
FILE: translations/sr/README.md
================================================
# Вавилон Приручник
Овај приручник је подељен у два дела:
* [Кориснички Приручник](user-handbook.md) - Како да подесите/прилагодите Вавилон и више.
* [Плагин Приручник](plugin-handbook.md) - Како да креирате плагинове за Вавилон.
> За будућа ажурирања, пратите [@thejameskyle](https://twitter.com/thejameskyle) на Твитеру.
Ако читате не-енглески превод овог приручника, још увек можете пронаћи делове на енглеском који још увек нису преведени. Ако желите да допринесете преводу на неки од других језика, морате то урадити кроз Crowdin. Молимо вас, прочитајте [водич за учешће](/CONTRIBUTING.md) за више информација. Пронаћи ћете одређен број енглеских речи које су програмерски концепти. Ако би се они преводили на друге језике, јавио би се недостатак конзистентности и течности када читате о њима. У највећем броју случајева, наћи ћете буквални превод праћен енглеским изразом у заградама `()`. На пример: Апстрактна синтаксна стабла (Abstract Syntax Trees).
================================================
FILE: translations/sr/plugin-handbook.md
================================================
# Babel Plugin Handbook
U dokumentu je objašnjeno kako se kreiraju [Babel](https://babeljs.io) [plaginovi](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
This handbook is available in other languages, see the [README](/README.md) for a complete list.
# Sadržaj
* [Uvod](#toc-introduction)
* [Osnove](#toc-basics)
* [AST strukture](#toc-asts)
* [Stanja pri kompajliranja (Stages of Babel)](#toc-stages-of-babel)
* [Parsiranje](#toc-parse)
* [Leksička analiza (Lexical Analysis)](#toc-lexical-analysis)
* [Analiza sintakse (Syntactic Analysis)](#toc-syntactic-analysis)
* [Transformisanje (Transform)](#toc-transform)
* [Generisanje (Generate)](#toc-generate)
* [Prolazak (Traversal)](#toc-traversal)
* [Posetioci (Visitors)](#toc-visitors)
* [Putanje (paths)](#toc-paths)
* [Putanje u "posetiocima"](#toc-paths-in-visitors)
* [Stanje (state)](#toc-state)
* [Domeni (scopes)](#toc-scopes)
* [Vezivanje (bindings)](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Defincije](#toc-definitions)
* [Gradioci (Builders)](#toc-builders)
* [Validatori](#toc-validators)
* [Konvertori](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Kreiranje vašeg prvog Babel plugina](#toc-writing-your-first-babel-plugin)
* [Operacije transformisanja](#toc-transformation-operations)
* [Posećivanje (visiting)](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulacija](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Domen](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plagin opcije](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Kreiranje čvorova](#toc-building-nodes)
* [Praktični saveti](#toc-best-practices)
* [Izbegavajte prolazak kroz AST što je više moguće](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Spajanje "posetioca" kad je to moguće](#toc-merge-visitors-whenever-possible)
* [Izbegavajte prolaske kada može da se upotrebi ručno prolaženje (kroz čvorove)](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizacija ugnežđenih "posetioca"](#toc-optimizing-nested-visitors)
* [Obratite pažnju na ugnežđene strukture](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Uvod
Babel je generički višenamenski kompajler za JavaScript. Sastoji se od kolekcije modula koji se mogu koristiti u različitim vidovima statičke analize koda.
> Statička analiza je proces analiziranja koda bez njegovog izvršavanja. (Analiza koda prilikom njegovog izvršavanja se naziva dinamička analiza). Postoji veliki broj primena statičke analize koda. Može se koristiti za proveru stila i načina pisanja (linting), kompajliranje, predstavljanje sintakse jezika bojama (color highlighting), transformisanje koda, optimizacije, minifikacije i druge operacije.
Uz pomoć Babel-a možete napisati mnoštvo različitih tipova alatki koje mogu da se koriste da povećaju produktivnost i poboljšaju kvalitet pisanog koda.
> ***За будућа ажурирања, пратите [@thejameskyle](https://twitter.com/thejameskyle) на Твитеру.***
* * *
# Osnove
Babel je JavaScript kompajler, tačnije kompajler iz koda u kod, što se najčešće naziva "transpiler". Drugim rečima, Babel može da modifikuje i da generiše potpuno novi kod na osnovu vašeg koda.
## AST strukture
Svaki od koraka pri kompajliranju uključuje kreiranje ili korišćenje apstraktnog sintaksnog stabla [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) tj. AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Da biste stekli bolji osećaj u vezi sa strukturama koje koristi AST možete da pogledate [AST Explorer](http://astexplorer.net/). Da biste pogledali kod iz prethodnog primera u Ast Exploreru možete da odete na [ovaj](http://astexplorer.net/#/Z1exs6BWMq) link.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
ili kao JavaScript objekt:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Svaki nivo AST strukture ima sličan oblik:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Napomena: neki delovi objekta (properties) su izostavljeni radi lakšeg uočavanja trenutno bitnih delova strukture.
Struktura predstavljenja u prethodnim primerima se naziva **čvor** (node). AST može da se sastoji bilo od jednog, bilo od ogromnog broja čvorova (nodes). Zajedno, svi čvorovi opisuju sintaksu programa, pa datu strukturu možemo da se iskoristimo za statičku analizu.
Svaki čvor je predstavljen na sledeći način:
```typescript
interface Node {
type: string;
}
```
Polje `type` je string koji sadrži tip objekta, odnosno čvora. (npr. `"FunctionDeclaration"`, `"Identifier"`, ili `"BinaryExpression"` - deklaracija fukcije, indetifikatora ili binarnog izraza/operacije). Svaki tipova čvora ima različitu dodatnu grupa podataka koji ga opisuju.
Za svaki čvor koji je generisan Babelom koriste se dodatni podaci koji sadrže informaciju o položaju čvora u originalnom kodu.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Polja (properties) `start`, `end`, `loc` se mogu naći u svakom pojedinačnom čvoru.
## Stanja pri kompajliranja (Stages of Babel)
Tri osnovna stanja kroz koje Babel prolazi su **parsiranje**, **transformisanje**, **generisanje**.
### Parsiranje
U fazi **parsiranja**, kod se pretvara u AST strukturu. Parsiranje čine dve faze: [**Leksička analiza**](https://en.wikipedia.org/wiki/Lexical_analysis) (Lexical Analysis) i [**Analiza sintakse**](https://en.wikipedia.org/wiki/Parsing) (Syntactic Analysis).
#### Leksička analiza (Lexical Analysis)
U leksičkoj analizi delić koda se pretvara u niz **tokena**.
Tokene možete da zamislite kao linearan niz sačinjen od delova koji čine sintaksu jezika (npr. operatori ili ključne reči).
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
U svakom `tipu` imamo skup polja (properties) koje opisuju dati token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Kao i u elementima AST strukture i ovde imamo polja `start`, `end`, and `loc`.
#### Analiza sintakse (Syntactic Analysis)
Analiza sintakse koristi niz tokena i pretvara ih u AST formu. U ovoj fazi tokeni su, na osnovu informacija koje nose, restruktuirani u formu AST-a. Ovakva reprezentacija strukture koda je daleko jednostavnija za dalje procesiranje.
### Transformisanje (Transform)
U stanju [transformisanja](https://en.wikipedia.org/wiki/Program_transformation) Babel prolazi kroz čvorove AST strukture i pri tom kreira nove, briše ili modifikuje postojeće čvorove. Ovo je daleko najsloženiji deo kroz koji prolazi bilo Babel bilo koji drugi kompajler. Plaginovi svoje procese obavljaju u ovom stanju pa će ovo stanje biti glavna tema u većem delu ovog priručnika. Zbog toga nećemo ulaziti duboko u detalje za sad.
### Generisanje (Generate)
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Generisanje koda je prilično jednostavan proces: prolazi se kroz AST strukturu i ispišu stringovi koji predstavljaju transformisani kod.
## Prolazak (Traversal)
Pri transformaciji AST strukture potrebno je rekurzivno [proći kroz stablo](https://en.wikipedia.org/wiki/Tree_traversal) kojim je predstavljen.
Recimo za tip `FunctionDeclaration` je karakteristično nekoliko podataka: `id`, `params`, i `body`. Svaki od njih ima ugnežđene druge čvorove.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Polazimo od `FunctionDeclaration` i pošto znamo polja njegove unutrašnje strukture, prolazimo redom kroz svako od tih polja i sve njihove podstruture (children).
Zatim dolazimo do `id` koji predstavlja `identifikator` (Identifier). `Identifikatori` nemaju podstrukturu pa možemo da nastavimo dalje sa prolaskom.
Sledi `params` koji je predstaljen nizom čvorova pa ćemo da prođemo kroz svakog od njih. U ovom slučaju nailazimo samo na jedan čvor koji je takođe `identifikator` pa možemo da nastavimo dalje.
Tada nailazimo na `body` koji je tipa `BlockStatement` i ima polje `body` koje predstavlja niz čvorova pa ćemo da prođemo kroz svaki od članova niza.
Jedini član niza je tipa `ReturnStatement` koji ima čvor tipa `argument`. Prolazeći kroz njega nailazimo na čvor tipa `BinaryExpression`.
Struktura `BinaryExpression` ima polja `operator`, a `left`, i a `right`. Polje operator nije čvor nego primitivni tip, pa nastavljamo dalje prolazak kroz `left` i `right`.
Opisani proces prolaska se desava u stanju transformisanja (transform stage).
### Posetioci (Visitors)
Kada govorimo o "prolasku" kroz čvor, zapravo mislimo na njihovo **podsećivanje**. Ovaj termin se korisiti zato što postoji koncept [**visitora**](https://en.wikipedia.org/wiki/Visitor_pattern) (visitor).
Posetioci (visitors) su algoritam (pattern) koji se koristi u AST za prolaske u raznim jezicima. Jednostavno rečeno, radi se o objektima sa definisanim metodama za prihvaćanje određenog tipa nodova u stablu. Kako sam koncept deluje pomalo apstraktno, pogledaćemo jedan primer.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Napomena:** `Identifier() { ... }` je skraćeno zapisano `Identifier: { enter() { ... } }`.
Ovo je osnovni posetioc (visitor). Prilikom prolaska (traversal) se za svaki `Identifier` u stablu poziva metod `Identifier()`.
To znači da će, u datom primeru, metod `Identifier()` biti pozvan četiri puta za svaki `Identifier` (uključujući `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
Svi ovi pozivi se dešavaju pri **ulasku** u čvor. Međutim postoji i mogućnost pozivanja metoda posetioca i pri **izlasku** iz čvora.
Zamislimo da imamo ovakvu strukturu:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
Pri prolasku kroz grana u stablu, nailazićemo na poslednje čvorove u datom nivou i tada treba da se vratimo na prethodni nivo u stablu da bismo došli do preostalih čvora. Idući kroz stablo **ulazićemo** u onda pri povratku nazad **izlazićemo** iz svakog od čvorava.
Pogledajmo, *korak po korak*, kako izgleda proces prolaska kroz stablo dato u poslednjem primeru.
* Ulaz u `FunctionDeclaration`
* Ulaz u `Identifier (id)`
* Završeno procesiranje čvora
* Izlaz iz `Identifier (id)`
* Ulaz u `Identifier (params[0])`
* Završeno procesiranje čvora
* Izlaz iz `Identifier (params[0])`
* Ulaz u `BlockStatement (body)`
* Ulaz u `ReturnStatement (body)`
* Ulaz u `BinaryExpression (argument)`
* Ulaz u `Identifier (left)`
* Završeno procesiranje čvora
* Izlaz iz `Identifier (left)`
* Ulaz u `Identifier (right)`
* Završeno procesiranje čvora
* Izlaz iz `Identifier (right)`
* Izlaz iz `BinaryExpression (argument)`
* Izlaz iz `ReturnStatement (body)`
* Izlaz iz `BlockStatement (body)`
* Izlaz iz `FunctionDeclaration`
Pri kreiranju posetioca postoje dve mogućnosti da posećivanje čvora.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Putanje (paths)
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Putanje u "posetiocima"
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Stanje (state)
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Domeni (scopes)
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Vezivanje (bindings)
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Napomena: Ovaj dokument ne zamenjuje detaljnu API dokumentaciju koja će uskoro biti dostupna na drugom mestu.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Napomena:** podrazumevana vrednost za `sourceType` je `"script"` u ako se pri parsiranju naiđe na ključnu reč `import` or `export` parser će da izbaci poruku o grešci i prekinuti parsiranje. Da bi izbegli ove greške koristite `sourceType: "module"` u opcijama pri pozivu parsera.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Defincije
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Gradioci (Builders)
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validatori
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Konvertori
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Kreiranje vašeg prvog Babel plugina
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Operacije transformisanja
## Posećivanje (visiting)
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulacija
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Domen
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plagin opcije
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Kreiranje čvorova
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Praktični saveti
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Izbegavajte prolazak kroz AST što je više moguće
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Spajanje "posetioca" kad je to moguće
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Izbegavajte prolaske kada može da se upotrebi ručno prolaženje (kroz čvorove)
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizacija ugnežđenih "posetioca"
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Obratite pažnju na ugnežđene strukture
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/sr/user-handbook.md
================================================
# Вавилон Кориснички Приручник
Овај документ покрива све што сте увек желели да знате о коришћењу [Вавилона](https://babeljs.io) и повезаних алатки.
[](http://creativecommons.org/licenses/by/4.0/)
Овај приручник је доступан и у другим језицима, погледајте [README](/README.md) фајл за комплетну листу.
# Sadržaj
* [Uvod](#toc-introduction)
* [Постављање Вавилона](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Коришћење Вавилоновог CLI из пројекта](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Uvod
Вавилон (Babel) је генерички вишенаменски компајлер за ЈаваСкрипт. Док користите Вавилон, можете користити (и направити) следећу генерацију ЈаваСкрипта, као и следећу генерацију ЈаваСкрипт алатки.
ЈаваСкрипт као језик констатно еволуира, са новим спецификацијама и предлозима који се појављују редовно са новим функционалностима. Коришћење Вавилона ће вам дозволити да користите јако пуно ових функционалности годинама пре него што буду омогућене свуда.
Вавилон ово ради компајлирајући ЈаваСкрипт код писан по последњим стандардима у верзију која ће радити свуда данас. Овај просец је познат као изворни-у-изворни (Source-to-source) компајлирање, такође познат као транспајлирање.
На пример, Вавилон може транспајлирати нову ЕС2015 "стрела функција" синтаксу из овога:
```js
const square = n => n * n;
```
У следећу:
```js
const square = function square(n) {
return n * n;
};
```
Ипак, Вавилон може урадити много више од овога, зато што Вавилон има подршку за синтаксне екстензије као што је ЈСХ синтакса за Риект (React) и Флоу (Flow) синтаксна подршка за статичну проверу куцања.
Поред тога, све у Вавилону је једноставно плагин и сви могу креирати своје плагинове користећи пуну моћ Вавилона да ураде шта пожеле.
*И поред тога*, Вавилон је разврстан у велики број кор модула (core modules) које било ко може користити да направи следећу генерацију ЈаваСкрипт алатки.
Многи људи то и раде, екосистем који је изникао око Вавилона је огроман и јако разноврстан. Кроз овај приручник, ја ћу покрити уједно и како Вавилонове уграђене алатке раде, и са друге стране неке корисне ствари из заједнице.
> ***За будуће апдејтове, пратите follow [@thejameskyle](https://twitter.com/thejameskyle) на Твитеру.***
* * *
# Постављање (Setting up) Вавилона
Пошто ЈаваСкрипт заједница нема јединствени алат за грађење (build tool), оквир (framework), платформу (platform) итд., Вавилон има званичне интеграције за све главне алате. Све од Галпа (Gulp) до Браузерфаја (Browserify), од Ембера (Ember) до Метеора (Meteor), без обзира како ваше поставке изгледају, вероватно постоји званична интеграција.
У сврхе овог приручника, ми ћемо само покрити уграђене начине постављања Вавилона, али ви такође можете посетити интерактивну [сетап страницу](http://babeljs.io/docs/setup) за све интеграције.
> **Белешка:** Овај водич ће правити референце на алате из командне линије као што су `node` и `npm`. Пре него што наставите даље, требало би да познајете њихово коришћење.
## `babel-cli`
Вавилонов CLI је лак начин да компајлирате фајлове користећи Вавилон из командне линије.
Прво ћемо га инсталирати глобално како бисмо научили основе.
```sh
$ npm install --global babel-cli
```
Можемо компајлирати наш први фајл овако:
```sh
$ babel my-file.js
```
Ово ће приказати компајлирани аутпут директно у вашем терминалу. Како бисмо га уписали у фајл, ставићемо и `--out-file` или`-o`.
```sh
$ babel example.js --out-file compiled.js
# или
$ babel example.js -o compiled.js
```
Ако желимо да компајлирамо целу директорију у нову директорију можемо урадити то користећи `--out-dir` или`-d`.
```sh
$ babel src --out-dir lib
# или
$ babel src -d lib
```
### Коришћење Вавилонове CLI из пројекта
Иако *можете* инсталирати Вавилонов CLI глобално на вашој машини, много је боље инсталирати га **локално** за сваки пројекат посебно.
Постоје два главна разлога за ово.
1. Различити пројекти на истој машини могу зависити од различитих верзија Вавилона, дозвољавајући вам да их унапређујете један по један.
2. Значи да немате имплицитну зависност од окружења на ком радите. Ово чини ваш пројекат портабилнијим и лакшим за сетаповање.
Можемо инсталирати Вавилонов CLI локално користећи:
```sh
$ npm install --save-dev babel-cli
```
> **Белешка:** Пошто је иначе лоша идеја да користите Вавилон глобално, можда желите да деинсталирате глобалну копију користећи:
>
> ```sh
$ npm uninstall --global babel-cli
```
Пошто се деинсталација заврши, ваш `package.json` фајл би требало да изгледа овако:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Сада уместо да користимо Вавилон директно из командне линије, ми ћемо ставити наше команде у **нпм скрипту** која ће користити нашу локалну верзију.
Једноставно додајте `"scripts"` поље у ваш `package.json` фајл и ставите Вавилон команду унутар као `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Сада из терминала можемо покренути:
```js
npm run build
```
Ово ће покренути Вавилон на исти начин као раније, осим што сада користимо локалну копију.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***За будућа ажурирања, пратите [@thejameskyle](https://twitter.com/thejameskyle) на Твитеру.***
================================================
FILE: translations/sv-SE/README.md
================================================
# Babelhandbok
Denna handbok är indelad i två delar:
* [Användarhandbok](user-handbook.md) - installation/konfigurering av Babel och mycket mer.
* [Pluginhandbok](plugin-handbook.md) - hur man skapar plugins för Babel.
> För framtida uppdateringar, följ [@thejameskyle](https://twitter.com/thejameskyle) på Twitter.
Om du läser en icke-engelsk översättning av denna handbok kan du fortfarande hitta sektioner som ännu inte har översatts. Om du vill bidra till en av översättningarna måste du göra det genom Crowdin. Läs [riktlinjerna för att bidra](/CONTRIBUTING.md) för mer information. Det används ett antal engelska ord som är programmeringsbegrepp. Om dessa översattes till andra språk skulle det finnas en brist på konsekvens och flyt när man läser om dem. I många fall hittar du den bokstavliga översättningen följt av den engelska termen i parentes `()`. Till exempel: abstrakt syntaxträd (ASTs).
================================================
FILE: translations/sv-SE/plugin-handbook.md
================================================
# Babel Plugin handbok
Det här dokumentet tar upp hur man skapar [Babel](https://babeljs.io) [plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
Handboken finns på andra språk, se [README](/README.md) för en komplett lista.
# Innehållsförteckning
* [Introduktion](#toc-introduction)
* [Grunderna](#toc-basics)
* [ASTs](#toc-asts)
* [Stadier av Babel](#toc-stages-of-babel)
* [Parse](#toc-parse)
* [Lexikalisk analys](#toc-lexical-analysis)
* [Syntaktisk analys](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generera](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Sökväg](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Skriva din första Babel-Plugin](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Introduktion
Babel är en generisk multi-purpose kompilator för JavaScript. Utöver detta är Babel en samling av moduler som kan användas till många olika former av statisk analys.
> Med statisk analys innebär att analysera kod utan att köra den. (Analys av koden vid körning kallas dynamisk analys). Syftet med statisk analys varierar stort. Det kan användas för att "linta", kompilera, "kod-hightligt", kodtransformering, optimisering, minimering och mycket mer.
Med Babel kan du bygga olika typer av verktyg som hjälper dig att bli mer produktiv och skriva bättre program.
> ***För framtida uppdateringar, Följ [@thejameskyle](https://twitter.com/thejameskyle) på Twitter.***
* * *
# Grunderna
Babel är en JavaScript kompilator, särskilt en källa-till-källa kompilator, ofta kallad en "transpiler". Detta innebär att du ger Babel JavaScript-kod, där koden ändras och genererar den nya koden som skickas tillbaka.
## ASTs
Alla dessa steg innebär att skapa eller arbeta med ett [Abstrakt Syntax träd](https://en.wikipedia.org/wiki/Abstract_syntax_tree) eller AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Kolla in [AST Explorer](http://astexplorer.net/) för att få en bättre känsla för AST-noder. [Här](http://astexplorer.net/#/Z1exs6BWMq) är en länk till det där exempelkoden ovan klistrats in i.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Eller som JavaScript-objekt:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Du kommer att märka att varje nivå av AST har en liknande struktur:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Obs: Vissa egenskaper har tagits bort för enkelhet.
Alla dessa är kända som en **nod**. En AST kan bestå av en enda nod, eller hundratals om inte tusentals noder. Tillsammans kan de beskriva syntaxen för ett program som kan användas för statisk analys.
Varje nod har detta gränssnitt:
```typescript
interface Node {
type: string;
}
```
Fältet `typ` är en sträng som representerar den typ av nod objektet är (dvs. `"FunctionDeclaration"`, `"Identifier"`, eller `"BinaryExpression"`). Varje typ av nod definierar ytterligare en uppsättning egenskaper som beskriver den särskilda nodtypen.
Det finns ytterligare egenskaper på varje nod som Babel genererar vilket beskriver nodens position i den ursprungliga källkoden.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Dessa egenskaper `start`, `end`, `loc`, visas i varje enskild nod.
## Stadier av Babel
De tre primära stadierna av Babel är **parse**, **transform** och **generate**.
### Parse
**Parse** tar koden och genererar en AST. Det finns två faser av satsanalysering i Babel: [**Lexikal analys**](https://en.wikipedia.org/wiki/Lexical_analysis) och [**Syntaktisk analys**](https://en.wikipedia.org/wiki/Parsing).
#### Lexikalisk analys
Lexikalisk analys tar en kodsträng och omvandlar den till en ström av **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Syntaktisk analys
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transform
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generera
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Sökväg
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Skriva din första Babel-Plugin
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/sv-SE/user-handbook.md
================================================
# Babel användarhandbok
Det här dokumentet tar upp allt du någonsin skulle vilja veta om hur du använder [Babel](https://babeljs.io) och dess relaterade verktyg.
[](http://creativecommons.org/licenses/by/4.0/)
Handboken finns på andra språk, se [README](/README.md) för en komplett lista.
# Innehållsförteckning
* [Introduktion](#toc-introduction)
* [Ställa in Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Introduktion
Babel är en generisk "multi-purpose" kompilator för JavaScript. Med hjälp av Babel kan du använda (och skapa) de nästkommande generationerna av JavaScript och JavaScript-verktyg.
JavaScript som språk utvecklas ständigt med nya specifikationer, funktioner. Därpå kommer även ständigt nya förslag på hur språket kan vidareutvecklas. Med hjälp av Babel kan använda många av dessa funktioner år innan de är tillgängliga överallt.
Babel gör detta genom att omvandla JavaScript-kod som skrivs med de senaste standarderna till en version som fungerar överallt idag. Denna process kallas "source-to-source compiling", även känt som transpilering.
Till exempel skulle Babel kunna förvandla nya ES2015 pilfunktionssyntaxen från detta:
```js
const square = n => n * n;
```
Till detta:
```js
const square = function square(n) {
return n * n;
};
```
Men Babel kan göra mycket mer än så. Babel har stöd för syntaxtillägg såsom JSX för React och Flow för statisk typkontroll.
Än mer: allt i Babel är helt enkelt en plugin och alla kan gå ut och skapa egna plugins som använder den fulla kraften i Babel för att göra vad de vill.
*I ytterligare grad* så är Babel uppdelat i ett antal centrala moduler vilka vem som helst kan använda för att bygga nästa generation av JavaScript verktyg.
Många människor gör också det. Ekosystemet som har vuxit upp runt Babel är massivt och mycket varierande. I denna handbok jag kommer att ta upp hur inbyggda Babel-verktyg fungerar samt en del användbara saker från oberoende utvecklare.
> ***För framtida uppdateringar, följ [@thejameskyle](https://twitter.com/thejameskyle) på Twitter.***
* * *
# Ställa in Babel
Eftersom JavaScript utvecklare inte har ett enda byggverktyg, ramverk eller liknande så stödjer Babel officiellt integreringsmöjligheter för alla de mer populära verktygen. Allt från Gulp till Browserify, från Ember till Meteor. Det spelar ingen roll hur din setup ser ut: det finns lär finnas en officiellt stödd integreringsmöjlighet i Babel som tillgodoser dina behov.
Denna handbok kommer endast att ta upp de inbyggda sätten att konfigurera Babel, men du kan också besöka den interaktiva [installationssidan](http://babeljs.io/docs/setup) för alla att kunna konfigurera allt.
> **Obs:** Denna guide kommer att hänvisa till kommandoradsverktyg som `node` och `npm`. Innan du fortsätter något bör du vara bekväm med dessa verktyg.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***För framtida uppdateringar, följ [@thejameskyle](https://twitter.com/thejameskyle) på Twitter.***
================================================
FILE: translations/tr/README.md
================================================
# Babel El Kitabı
Bu el kitabı iki bölümden oluşur:
* [Kullanıcı el kitabı](user-handbook.md) - Kurulum yapılandırılması Babel ve daha fazlası.
* [Eklenti el kitabı](plugin-handbook.md) - Babel için nasıl eklenti oluşturulur.
> Gelecek güncellemeler için [@thejameskyle](https://twitter.com/thejameskyle)'i Twitter'dan takip edin.
Eğer bu el kitabının İngilizce olmayan bir çevirisini okuyorsanız, halen çevrilmemiş bölümler görebilirsiniz. Eğer bu çevirilerden birine katkıda bulunmak istiyorsanız, bunu Crowdin aracılığıyla yapmalısınız. Lütfen daha fazla bilgi için [katkıda bulunma rehberi](/CONTRIBUTING.md)ne göz atın. Bazı programlama konseptlerinin İngilizce olarak yazıldığını göreceksiniz. Eğer bunlar diğer dillere çevirilir ise okumada tutarlılık ve akıcılık eksikliği olacaktır. Birçok durumda parantez içindeki ingilizce terimin edebi çevirisini bulacaksınız.`()`. Örneğin: Soyut Sözdizimi Ağaçları (ASTs).
================================================
FILE: translations/tr/plugin-handbook.md
================================================
# Babel Handbook eklentisi
Bu belge [Babel ](https://babeljs.io) [eklentisinin](https://babeljs.io/docs/advanced/plugins/) nasıl oluşturulacağını kapsar..
[](http://creativecommons.org/licenses/by/4.0/)
Bu el kitabı diğer diller için de mevcuttur, görmek için README > bölümüne bakın. Bütün liste.
# içindekiler
* [Başlangıç](#toc-introduction)
* [Temel](#toc-basics)
* [ASTs](#toc-asts)
* [Babel aşamaları](#toc-stages-of-babel)
* [İncelemek](#toc-parse)
* [Sözlük analizi](#toc-lexical-analysis)
* [Sözdizimsel analiz](#toc-syntactic-analysis)
* [Dönüştür](#toc-transform)
* [Oluştur](#toc-generate)
* [Geçmek](#toc-traversal)
* [Ziyaretçiler](#toc-visitors)
* [Patika](#toc-paths)
* [Ziyaretçi Yolları](#toc-paths-in-visitors)
* [Durum](#toc-state)
* [Kapsam](#toc-scopes)
* [Bağlamak](#toc-bindings)
* [UPA](#toc-api)
* [Babil](#toc-babylon)
* [Uğultuyu Geçmek](#toc-babel-traverse)
* [Uğultu Tipi](#toc-babel-types)
* [Tanım ](#toc-definitions)
* [İşçiler](#toc-builders)
* [Yetkili](#toc-validators)
* [Çeviriciler](#toc-converters)
* [babel oluşturucu](#toc-babel-generator)
* [babel tema](#toc-babel-template)
* [İlk Babel eklentinizi yazın](#toc-writing-your-first-babel-plugin)
* [Dönüşüm işlemleri](#toc-transformation-operations)
* [Ziyaret ediliyor](#toc-visiting)
* [İyi Noktaya Gelmenin Yolunu Bul](#toc-get-the-path-of-a-sub-node)
* [Bir düğümün belirli bir tür olup olmadığını kontrol edin](#toc-check-if-a-node-is-a-certain-type)
* [Bir yolun belirli bir tür olup olmadığını kontrol edin](#toc-check-if-a-path-is-a-certain-type)
* [Bir belirleyicinin referanslı olup olmadığını kontrol edin](#toc-check-if-an-identifier-is-referenced)
* [Belirli bir anayol bul](#toc-find-a-specific-parent-path)
* [Kardeş yollar edinin](#toc-get-sibling-paths)
* [Dolaşımı durdur](#toc-stopping-traversal)
* [Manipülasyon](#toc-manipulation)
* [Bir düğümü değiştir](#toc-replacing-a-node)
* [Bir düğümü birden fazla düğümle değiştir](#toc-replacing-a-node-with-multiple-nodes)
* [Bir düğümü kaynak dizesi değiştir](#toc-replacing-a-node-with-a-source-string)
* [Kardeş düğüm ekle](#toc-inserting-a-sibling-node)
* [Bir konteyner içine yerleştir](#toc-inserting-into-a-container)
* [Düğüm kaldır](#toc-removing-a-node)
* [Velisini Sayıklamak](#toc-replacing-a-parent)
* [Velisini Terk Etmek](#toc-removing-a-parent)
* [Kapsam](#toc-scope)
* [Veriler Karşılanıyorsa Onayla](#toc-checking-if-a-local-variable-is-bound)
* [Kullanıcı Arayüzü Oluşturmak](#toc-generating-a-uid)
* [Değişken bildirimi bir üst kapsama gönderiliyor](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Bağlantı ve Referanslarını Yeniden adlandır](#toc-rename-a-binding-and-its-references)
* [Eklenti Seçenekleri](#toc-plugin-options)
* [Eklentilerin öncesi ve sonrası](#toc-pre-and-post-in-plugins)
* [Syntax eklentisini aktifleştir](#toc-enabling-syntax-in-plugins)
* [Bağlantıları yapmak](#toc-building-nodes)
* [En iyi pratik](#toc-best-practices)
* [AST 'yi geçmekten mümkün olduğunca kaçının](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Mümkün olunca ziyaretçileri birleştir](#toc-merge-visitors-whenever-possible)
* [Manüel arama yapılırsa çaprazlama yapmayın](#toc-do-not-traverse-when-manual-lookup-will-do)
* [İç içe geçmiş ziyaretçiler optimize ediliyor](#toc-optimizing-nested-visitors)
* [İç içe geçmiş yapıları fark edin](#toc-being-aware-of-nested-structures)
* [Ünite Testi](#toc-unit-testing)
# Başlangıç
Babel, JavaScript için genel bir çok amaçlı derleyicidir. Dahası, birçok farklı statik form analizi için kullanılabilen modül koleksiyonudur.
> Statik analiz, kodları çalıştırmadan analiz eder. (Kodların analizi yapılırken bunlar dinamik analiz olarak tanınır). Statik analizin amaçları büyük ölçüde değişir. Bu linting, derleme, kod vurgulama, kod dönüştürme, optimize, minimize ve daha fazlası için kullanılabilir.
Daha iyi ve daha üretken programlar yazmak, size yardımcı olabilecek bir çok farklı araç türü oluşturmak için Babel'i kullanabilirsiniz.
> ***Gelecek güncellemeler için [@thejameskyle](https://twitter.com/thejameskyle)'i Twitter'dan takip edin.***
* * *
# Temel
Babel JavaScript derleyicidir, özellikle kaynaktan kaynağa derleyicidir, çoğunlukla "transpiller" denilir. Bu şu anlama gelir, "Siz Babel'e bazı JavaScript kodlarını verirsiniz, Babel bunları değiştirip size yeni kodlar verir".
## ASTs
Bu adımların her biri " [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) veya AST " oluşturur veya beraber çalışır.
> Babel, [ESTree](https://github.com/estree/estree) 'den dönüştürülmüş AST kullanır, çekirdek spesifikasyonu belirtilmiş [here](https://github.com/babel/babylon/blob/master/ast/spec.md) ile.
```js
işlev kare(n) {
dön n * n;
}
```
> AST düğümlerini daha iyi anlamak için " [AST Explorer](http://astexplorer.net/) " kontrol edin. [Here](http://astexplorer.net/#/Z1exs6BWMq), yukarıdaki örnek kod ile yapıştırılan bir bağlantıdır.
Bu aynı program böyle bir ağaç gibi temsil edilebilir:
```md
-İşlevBeyanı:
-kimliği
-tanımlayıcı
-isim: kare
-parametreler [1]
-tanımlayıcı
-name: n
-vücut:
-Blokİfadesi
-vücut [1]
-Dönüşİfadesi
-argüman
-İkiliİfade
-operatör: *
-sol
-tanımlayıcı
-isim: n
-sağ
-tanımlayıcı
-isim: n
```
Yada bu şekilde JavaScript nesnesi olarak:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
AST'nin her bir seviyesinin benzer bir yapıya sahip olduğunu fark edeceksiniz:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Not: Bazı yapılar basitlik için kaldırılmıştır.
Bunların her biri **Node** olarak biliniyor. AST tek bir düğümden oluşabilir, veya yüzlerce, binlerce düğüm değilse. Statik analiz için kullanılabilen bir programın söz dizimini birlikte tanımlayabilirler.
Her düğüm bu arayüze sahiptir:
```typescript
interface Node {
type: string;
}
```
`type` alanı, nesnenin bulunduğu düğüm türünü temsil eden bir dizedir (ör. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Her düğüm türü, belirli düğüm türünü tanımlayan ek bir özellik kümesi tanımlar.
Her düğümde Babel'in oluşturduğu ek özellikler vardır. Düğümün orijinal kaynak kodundaki konumunu tanımlar.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
Bu özellikler `start`, `end`, `loc`, her bir düğümde görülür.
## Babel aşamaları
Babel 'in üç ana aşaması **parse**, **transform**, **generate**.
### İncelemek
**parse** aşaması, kod alır ve bir AST çıktılar. Babel 'de ayrıştırma için iki faz vardır [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) ve [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Sözlük analizi
Sözcüksel Analiz, bir dizi kod alır ve onu bir akış haline getirir. **tokens**.
İşaretleri düz bir dil söz dizimi dizisi olarak düşünebilirsiniz.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
`type`s her biri burada belirteci tanımlayan bir özellik kümesine sahip:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
AST düğümleri gibi ayrıca `start`, `end`, and `loc` var.
#### Sözdizimsel analiz
Söz dizimsel Analiz, belirteç akışı alıp bir temsili AST'ye dönüştürür. Belirteçlerdeki bilgileri kullanma, onları çalışmak için daha kolay olan kodun yapısını temsil eden bir AST olarak kodlayan bir yapıya dönüştürür.
### Dönüştür
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. Burası eklentilerin yönetildiği yer ve bu kitapçığın bir çok başlığı bu olacak. Şuan için çok derine inmek istemiyoruz.
### Oluştur
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Geçmek
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Ziyaretçiler
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
işlev kare(n) {
dön n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Patika
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Ziyaretçi Yolları
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Durum
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Kapsam
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bağlamak
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# UPA
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Tanım
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### İşçiler
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Yetkili
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Çeviriciler
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# İlk Babel eklentinizi yazın
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Dönüşüm işlemleri
## Ziyaret ediliyor
### İyi Noktaya Gelmenin Yolunu Bul
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Bir düğümün belirli bir tür olup olmadığını kontrol edin
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Bir yolun belirli bir tür olup olmadığını kontrol edin
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Bir belirleyicinin referanslı olup olmadığını kontrol edin
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Belirli bir anayol bul
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Kardeş yollar edinin
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Dolaşımı durdur
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Bir düğümü değiştir
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Bir düğümü birden fazla düğümle değiştir
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Bir düğümü kaynak dizesi değiştir
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Kardeş düğüm ekle
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Bir konteyner içine yerleştir
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Düğüm kaldır
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Velisini Sayıklamak
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Velisini Terk Etmek
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Veriler Karşılanıyorsa Onayla
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Kullanıcı Arayüzü Oluşturmak
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Değişken bildirimi bir üst kapsama gönderiliyor
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Bağlantı ve Referanslarını Yeniden adlandır
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Eklenti Seçenekleri
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Eklentilerin öncesi ve sonrası
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Syntax eklentisini aktifleştir
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Bağlantıları yapmak
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# En iyi pratik
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## AST 'yi geçmekten mümkün olduğunca kaçının
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Mümkün olunca ziyaretçileri birleştir
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Manüel arama yapılırsa çaprazlama yapmayın
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## İç içe geçmiş ziyaretçiler optimize ediliyor
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## İç içe geçmiş yapıları fark edin
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Ünite Testi
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/tr/user-handbook.md
================================================
# Babel Kullanıcı Elkitabı
Bu belge [Babel](https://babeljs.io) ve ilgili araçları ile ilgili bilmek isteyeceğiniz herşeyi kapsar.
[](http://creativecommons.org/licenses/by/4.0/)
Bu kitabın diğer dillere tercümeside mevcuttur.Tüm liste için [README](/README.md)'ye bakın.
# İçindekiler
* [Giriş](#toc-introduction)
* [Babel Kurulumu](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Proje içinde Babel CLI çalıştırmak](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Babel Yapılandırması](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Üretilen kodu çalıştırma](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Babel (Gelişmiş) yapılandırma](#toc-configuring-babel-advanced)
* [Eklentileri el ile belirtme](#toc-manually-specifying-plugins)
* [Eklenti seçenekleri](#toc-plugin-options)
* [Ortama göre Babel'i özelleştirme](#toc-customizing-babel-based-on-environment)
* [Kendi önceden belirlenmiş ayarlarınızı yapmak](#toc-making-your-own-preset)
* [Babil ve diğer araçlar](#toc-babel-and-other-tools)
* [Statik analiz araçları](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Kod stili](#toc-code-style)
* [Dökümantasyon](#toc-documentation)
* [Frameworkler](#toc-frameworks)
* [React](#toc-react)
* [Metin düzenleyicileri ve IDE'ler](#toc-text-editors-and-ides)
* [Babel destek](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel sohbet](#toc-babel-chat)
* [Babel sorunları](#toc-babel-issues)
* [Harika bir Babil hata raporu oluşturma](#toc-creating-an-awesome-babel-bug-report)
# Giriş
Babel genel -jenerik- (ve) çok amaçlı Javascript derleyicisidir. Babel kullanarak gelecek nesil Javascript -dilinin- yanısıra gelecek nesil Javascript araçlarını kullanabilir (ve oluşturabilirsiniz).
Bir dil olarak Javascript, yeni bildirge ve önerilerler ile ortaya çıkan yeni özellikleriyle sürekli gelişmektedir. Babel kullanmak bu yeni özelliklerden çoğunun yayılmasından yıllar önce kullanmanıza imkan verir.
Babel bunu yazılmış Javascript kodunu bugün her yerde çalışabilecek geçerli standart versiyona dayalı olarak derleyerek yapar. Bu işlem kaynaktan kaynağa derleme (source-to-source compiling) veya başka bir ismiyle transpiling olarak adlandırılır.
Örneğin Babel yeni ES2015 küçüktür (işaretli) aşağıdaki fonksiyonu:
```js
const square = n => n * n;
```
aşağıdaki fonksiyona dönüştürür:
```js
const square = function square(n) {
return n * n;
};
```
Ancak, Babel bundan daha fazlasını yapabilir ki Babel'in React için JSX sözdizimi ve static tip kontrolü için Flow sözdizimi desteği gibi sözdizimi uzatı - syntax extension- desteği mevcuttur.
Buna ek olarak , Babel içindeki her şey sadece eklentidir - plugin- ve herkes -Babel'in- dışına çıkabilir ve istedikleri ne varsa yapabilmek için Babel'in tüm gücünü ikullanarak kendi eklentilerini yazabilirler.
Daha da fazlası, herkesin yeni bir javascript aracı -javascript tooling- hazırlayabilmesi için Babel bir çok çekirdek modüllere ayrılmıştır.
Birçok kişinin yaptıklarıyla - katkılarıyla - , Babel etrafında büyük ve çeşitli bir ekosistem -gelişip- çoğalmaktadır. Bu el kitabı -handbook- boyunca , Babel'in yerleşik (built-in) araçlarının nasıl çalıştığının yanısıra topluluk tarafından -keşfedilen- kullanışlı özellikleri de ele alacağız.
> ***Gelecek güncellemeler için [@thejameskyle](https://twitter.com/thejameskyle)'i Twitter'dan takip edin.***
* * *
# Babel Kurulumu
Javascript topluluğunun tek bir kurma aracı -build tool- ,çatı yapısı -framework , platformu gibi - özellikleri yok iken - Babel'in tüm ana araçlar -major tooling- için resmi entegrasyonu bulunmaktadır. Glup'tan Browserify'a , Ember'den Meteor'a kurulumunuzda ne olursa olsun muhtemelen resmi entegrasyonu bulunmaktadır.
Bu el kitabının amacı uyarınca, Babel'i yerleşik (built-in) yolları kullanarak hazırlmayı kapsayacaktır fakat isterseniz tüm entegrasyon için interaktif [kurulum sayfasını](http://babeljs.io/docs/setup) kullanabilirsiniz.
> **Not:** Bu kılavuz `node` ve `npm` gibi komut satırı -command line- araçlarını referanslar gönderir. Daha ileri de zorluk çekmemek için bu araçları anlamış ve kullanabiliyor olmanız gerekir.
## `babel-cli`
Babel CLI (Babel Komut Satırı arayüzü) Babel ile dosyaları komut satırı üzerinden derlemenin basit yoludur.
Sistem üzerinde global olarak yükleyerek temelleri öğrenelim.
```sh
$ npm install --global babel-cli
```
İlk dosyanmızı şu şekilde derleyebiliriz.
```sh
$ babel my-file.js
```
Bu konut derlenmiş çıktıyı terminal üzerine hemen yazar. Bunu bir dosyaya yazmak için `--out-file`veya `-o` önekini yazmamız gerekir. .
```sh
$ babel example.js --out-file compiled.js
# veya
$ babel example.js -o compiled.js
```
Eğer tüm klasörü yeni bir klasöre derlemek için : `--out-dir` or `-d` komutlarını kullanabiliriz..
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Proje içinde Babel CLI çalıştırmak
Babel CLI sisteminize global olarak yükleyebilmeniz mümkün olmasına rağmen, her bir projeniz için ayrı ayrı yüklemeniz daha kullanışlı olur.
Bunun iki önemli sebebi vardır:
1. Aynı sistemdeki farklı projeler Babel'in farklı versiyonlarına bağlı olabilir . Babel bunları ayrı ayrı güncellemenize olanak tanır.
2. Bu çalıştığınız çevre için gizli bağımlılıklara sahip olmadığınız anlamına gelir. Projeleriniz çok daha taşınabilir ve kolaylıkla kurulabilir hale getirir.
Yerel - Local- olarak Babel CLI'ı şu şekilde kurarız:
```sh
$ npm install --save-dev babel-cli
```
> **Not:** Babel'i -sistem üzerinde- global kurmak kötü bir fikir olduğundan , aşağıdaki komutu girerek sistemden kaldırabilirsiniz:
>
> ```sh
$ npm uninstall --global babel-cli
```
Yükleme bittikten sonra `package.json` şu şekilde görülmelidir.
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Şimdi Babali dolaysız olarak komut satırından kullanmaktansa , komutlarımızı yerel versiyonumuzda kullandığımız **npm scriptine** koyacağız.
Basitce `package.json` dosyası içinde `"scripts"` alanını ekleyin ve babel komutlarınızı bu alan içinde `build` alanı ekleyerek yazınız. .
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Şimdi terminal üzerinden aşağıdaki komutu çalıştırabiliriz:
```js
npm run build
```
Bu daha önceden yaptığımız gibi Babel'i çalıştıracak , -fakat bu sefer - yerel -Babel - kopyası üzerinden -işlem - gerçekleşecek
## `babel-register`
Sonraki Babel'i çalıştıran en geçerli metod `babel-register`dır. Bu seçenek sadece kurulumunuzla daha iyi entegre olabilecek gerekli dosyalarla Babel'i çalıştırmanıza izin verir.
Unutmayın ki bu üretim seviyesi kullanımı anlamına gelmez. Bu şekilde dağıtım için derleme yapmak hatalı görülür. Dağıtım vaktinden önce derlemek çok daha iyidir. Ancak bu işlemler kurulum dosyaları -build script - veya yerel olarak yaptığınız işler için oldukça iyi çalışır.
Önce projemiz içinde `index.js` dosyasını oluşturalım
```js
console.log("Hello world!");
```
Eğer bu -dosyayı- `node index.js` ile çalıştırırsak , bu işlem Babel ile derlenmeyecektir. Bunun yerine `babel-register` ile kurulum yapacağız..
Önce `babel-register` 'ı yükleyelim..
```sh
$ npm install --save-dev babel-register
```
Sonra , `register.js` dosyasını projenizde oluşturun ve aşağıdaki kodu ekleyin:
```js
require("babel-register");
require("./index.js");
```
Bu Babel'i Node modül sistemine *kaydeder* ve her bir `gerekli dosyayı `derlemeye başlar.
Şimdi `node index.js` olarak dosyayı yürütmek yerine `register.js` dosyasını kullanabiliriz.
```sh
$ node register.js
```
> **Node Babeli'i derlemek istediğiniz dosya üzerinde kayıt edemezsiniz.Node dosyayı Babel'in -önceden- derleme şansı olmadan yürütmeye başlar
>
> ```js
require("babel-register");
// derleme hatası verir !! :
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Unutmayın ki bu üretim seviyesi kullanımı anlamına gelmez. Bu şekilde dağıtım için derleme yapmak hatalı görülür. Dağıtım vaktinden önce derlemek çok daha iyidir. Ancak bu işlemler kurulum dosyaları -build script - veya yerel olarak yaptığınız işler için oldukça iyi çalışır.
Önce `babel-cli` 'ın sisteminizde kurulu olduğundan emin olalım:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Daha sonra nerede `node` kullandığunız yerlerin yerine `babel-node` yazınız..
Eğer npm `scriptleri ` kullanıyorsanız şu şekilde - değişiklik- yapabilirsiniz.
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Bu hallerin dışında -çalışma- yolunu `babel-node` üzerine yazmanız gerekir.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> İpucu: Ayrıca [`npm-run`](https://www.npmjs.com/package/npm-run) kullanabilirsiniz.
## `babel-core`
Herhangi bir sebebten dolayı Babel'i program arayüzüyle kullanacaksanız `Babil-çekirdek` paketinin kendisini kullanabilirsiniz.
Önce `babel-register` 'ı yükleyelim..
```sh
$ npm install babel-core
Context | Request Context
```
```js
var babel = require("babel-core");
```
Eğer -dosya yerine- yazı halinde derlenecek Javascript dosyanız varsa `babel.transform` ile doğrudan derleme yapabilirsiniz..
```js
babel.transform("code();", options);
// => { code, map, ast }
```
Eğer dosyalar üzerinde çalışıyorsanız eşzamansız (asynchronous) api (kullanıcı program arayüzünü) de kullanabilirsiniz.
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
yada eşzamanlı (synchronous api) api'yi
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
Daha önceden herhangi bir sebeble Babel AST -elinizde mevcutsa- AST üzerinden doğrudan dönüşüm yapabilirsiniz.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Babel Yapılandırması
Şu ana kadar Babel'in javascript dosyalarını bir yerden diğerine kopyalamaktan başka bir şey yapmadığını farketmiş olmalısınız.
Bunun sebebi Babel'in ne yapacağını size anlatmamış olmamız.
> Babel genel amaçlı derleyici olduğundan hiçbir -işlem- için varsayılan kullanım yolu belirtmez sayısız yoldan kullanma imkanı vardır. Babel'in ne yapacağını açıkça - explicitly- belirtmeniz gerekir.
**plugins** veya **presets** (Plugin grubu) yükleyerek Babel'e ne yapacağı ile ilgili talimatlar verebilirsiniz.
## `.babelrc`
Babel' e ne yapacağını söylemeden önce yapılandırma dosyası oluşturmalıyız. Tüm yapmanız gereken `.babelrc` dosyasını projenin kök dizininde oluşturmanız gerekir. Şöyle başlayabilirsiniz.:
```js
{
"presets": [],
"plugins": []
}
```
Bu dosya Babel'e istedğinizi yaptırabilmek için nasıl yapılandıracağınızı belirler.
> **Not:** Babel'i ayarlarmak için başka yollar olmasına rağmen , `.babelrc` dosyası esas ve en iyi yoldur.
## `babel-preset-es2015`
Babeli ES2015 (ES6 olarak da bilinen , en yeni Javascript standartı) standardından ES5 (bugün kullanılan en yaygın javascript standardına) derleme doğru derleme yaparak başlayalım.
Babel present alanına "es2015" yükleyelim:
```sh
$ npm install --save-dev babel-preset-es2015
```
Sonra , `.babelrc` dosyasını present (Plugin grubu) 'ı içerek şekilde değiştirelim.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
React 'ı kurmak çok kolaydır. Önce Preset olarak yükleyin :
```sh
$ npm install --save-dev babel-preset-react
```
`.babelrc` dosyasına işbu present 'ı ekleyin:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
Javascript'in TC39 (Ecmascript'in arkasındaki teknik komite) standartı üzerinden veren bazı öneriler vardır.
Bu işlem 5 aşamalı (0'dan 4'e ) aşamalara bölümüştür. Önerilen giderek daha fazla izlendikçe ve diğer standart aşamalarında kabul gördükçe yükselir ve 4. aşamada kullanıma açılır.
Bunlar Babel üzerinde 4 ayrı present (Plugin Grubu) olarak paketlenmiştir:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Not : 4. aşama yukarıda gösterilen `es2015` present 'i olduğundan ayrıca hazırlanmamıştır.
Her bir present aşaması daha sonraki preset aşamasında gereklidir. `babel-preset-stage-2` `babel-preset-stage-1` 'i gerektirirken `babel-preset-stage-3` tarafındandan da ihtiyaç duyulur..
Basitce hangi aşamaya ihtiyacınız varsa kurulumunu yapınız:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Sonra `.babelrc` ayar dosyasına ekleyiniz
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Üretilen kodu çalıştırma
Kodunuzu Babel'de derlediniz , fakat bu hikayenin sonu değildir.
## `babel-polyfill`
Hemen hemen gelecekte kullanılacak tüm Javascript söz dizimi (syntax) Babel ile derlenebilir fakat bu tüm API 'ler için geçerli değildir.
Örneğin , aşağıdaki kod derlenmesi gereken ok (arrow "=>") fonksiyonuna ihtiyaç duyar.
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Bu şu şekle dönüştürülür
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
Ancak bu kod her ortamda çalışmaz çünkü `Array.from` tüm Javascript ortamlarında mevcut değildir.
Uncaught TypeError: Array.from is not a function
Yakalanamamış Tip Hatası : Array.from fonksiyonu mevcut değil
Bu problemi çözmek için [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill) denen yaklaşımı kullanırız. Temel anlamıla polyfily çalışma zamanında mevcut olmayan tabii api'yi çoğaltan kod parçasıdır. bu size daha önceden kullanıma açılmamış `Array.from` gibi API'yi kullanmanıza izin verir.
Babel kusursuz [core.js](https://github.com/zloirock/core-js) ile birlikte çalışmakta olan oluşturucu ve eş zamansız fonksiyonları alabilmek için kişiselleştirilebirilir [yeniden oluşturucu -regenerator- ](https://github.com/facebook/regenerator) çalışma zamanını kendi polyfill 'i olarak kullanır.
Babel polyfill'i dahil etmek için önce npm ile sisteme yükleyelim :
```sh
$ npm install --save babel-polyfill
```
Daha sonra gerekli dosyaların giriş satırına pollyfill 'i dahil edelim.
```js
import "babel-polyfill";
```
## `babel-runtime`
ECMAScript yönerge detaylarını uygulamak -ve- üretilmiş kodları temiz tutabilmek için Babel "yardımcı -helper-" metodlarını kullanacaktır.
Bu "helpers - yardımcılar" oldukça uzun olduğundan ve her bir ihtiyaç duyulan dosyanın en üst satırına eklendiğinden bunları gerekli tek bir "runtime - çalışma zamanı- "na taşıyabilirsiniz.
`babel-plugin-transform-runtime` ve `babel-runtime`'mı yükleyerek başlayınız:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Şimdi Babel kodunuzu aşağıdaki koddan :
```js
class Foo {
method() {}
}
```
Şundan derleyebilir:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
`_classCallCheck` ve `_createClass` deklerasyon yardımcılarını her bir dosyaya koymak yerine gerektiği dosyalara konulabilir.
* * *
# Babel (Gelişmiş) yapılandırma
Çoğu kişi Babel'i yerleşik düzeniyle kullanabilir fakat babel bundan fazlasını sunmaktadır.
## Eklentileri el ile belirtme
Babel önceden ayarlanamış plugin setini sunuar. Eğer bunlardan hariç bir şekilde kullanmak isyitorsanız pluginleri kendiniz belirtebilirsiniz. Bu -yukarıda gördümüz- present 'larla aşağı yukarı aynı şekilde çalışmaktadır.
Önce plugin'i yükleyelim
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Sonra `plugins` alanını `babelrc` dosyanıza ekleyelim..
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
Bu size yürütmekte olduğunuz tüm dönüşümler üzerinde daha ince control imkanı sunar.
Tüm resmi plugin'ler için [Babel Plugins sayfasını](http://babeljs.io/docs/plugins/) ziyaret ediniz..
Ayrıca [topluluk tarafından oluşturulan ](https://www.npmjs.com/search?q=babel-plugin) pluginlere de göz atabilirsiniz. If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Eklenti seçenekleri
Çoğu pluginin varsayılandan farklı davranması için ayarlama imkanları vardır. Örneğin , çoğu dönünüşümün performans ve basitlik lehine bazı özellikleri görmezden geldiği "loose - hafif-" modu bulunmaktadır.
Plugin'e seçenekler eklemek için aşağıdaki değişimleri yapınız:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> Gelecek dönemde plugin dökümantasyonu için güncelllemeler yapacağım [ Beni takip ediniz](https://twitter.com/thejameskyle).
## Ortama göre Babel'i özelleştirme
Babel plugins çok çeşitli görevlerde kullanılır. Bunların çoğu kodunuzun hatalarını gidermek yada diğer araçlarla entegre olmak için size yardımcı olan geliştirici ayarlarıdır. Ayrıca bir çok plugin üretim zamanında kodunu optimize etmek için kullanılır.
Bu sebeble , sistemin durumuna göre Babel'i ayarlamak gerekebilir. Bu `.babelrc` dosyasıyla kolaylıkla yapılabilir.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel `env` alanının içinden cari ortama göre ayarlamaya izin verir.
Cari sistem ortamı için `process.env.BABEL_ENV` sabiti kullanılır. `BABEL_ENV` sabitinin olmadığı durumlarda `NODE_ENV` sabiti kullanır ve eğer bu "development" olarak atanmışsa mevcut değildir..
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` Babeli yürütmek için kullandığınız komut (örneğin e. `babel`, `babel-node`, veya eğer sicil üzerinden - register hook- çalıştırdıysanız sadece `node` )
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Kendi önceden belirlenmiş ayarlarınızı yapmak
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babil ve diğer araçlar
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Statik analiz araçları
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Kod stili
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Dökümantasyon
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworkler
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Metin düzenleyicileri ve IDE'ler
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel destek
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel sohbet
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel sorunları
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Harika bir Babil hata raporu oluşturma
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***Gelecek güncellemeler için [@thejameskyle](https://twitter.com/thejameskyle)'i Twitter'dan takip edin.***
================================================
FILE: translations/uk/README.md
================================================
# Довідник Babel
Цей довідник складається з двох частин:
* [Довідник користувача](user-handbook.md) - Як встановити/налаштувати Babel та інше.
* [Довідник розробника плагінів](plugin-handbook.md) - Як створювати плагіни для Babel.
> Ви можете слідкувати за оновленнями в Twitter, підписавшись на [@thejameskyle](https://twitter.com/thejameskyle).
Якщо ви читаєте не англійську версію цього довідника ви все одно можете зустріти секції англійською мовою які ще не перекладені. Ви можете приєднатись до роботи над перекладами за допомогою платформи Crowdin. Будь ласка, ознайомтесь із [довідкою перекладача](/CONTRIBUTING.md) для детальнішої інформації. Ви зустрінете цілий ряд англійських слів які описують концепції в програмуванні. Якщо вони будуть перекладені на інші мови це може спричинити непорозуміння, та зашкодити розумінню всього тексту. В багатьох випадках ви знайдете дослівний перклад поряд з оригінальною назвою в дужках `()`. Наприклад: Абстрактні Синтаксичні Дерева (ASTs).
================================================
FILE: translations/uk/plugin-handbook.md
================================================
# Довідник розробника плагінів до Babel
Цей документ містить інформацію про те, як створювати плагіни ([plugins](https://babeljs.io/docs/advanced/plugins/)) до [Babel](https://babeljs.io).
[](http://creativecommons.org/licenses/by/4.0/)
Цей довідник доступний також іншими мовами, повний список можна знайти у файлі [README](/README.md).
# Зміст
* [Вступ](#toc-introduction)
* [Основи](#toc-basics)
* [AST](#toc-asts)
* [Етапи Babel](#toc-stages-of-babel)
* [Аналіз](#toc-parse)
* [Лексичний аналіз](#toc-lexical-analysis)
* [Синтаксичний аналіз](#toc-syntactic-analysis)
* [Трансформація](#toc-transform)
* [Генерація](#toc-generate)
* [Обхід](#toc-traversal)
* [Відвідувачі](#toc-visitors)
* [Шляхи](#toc-paths)
* [Шляхи для Відвідувачів](#toc-paths-in-visitors)
* [Стан](#toc-state)
* [Області](#toc-scopes)
* [Прив'язки](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Визначення](#toc-definitions)
* [Конструктори](#toc-builders)
* [Валідатори](#toc-validators)
* [Перетворювачі](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Написання свого першого плагіну до Babel](#toc-writing-your-first-babel-plugin)
* [Опреації трансформації](#toc-transformation-operations)
* [Відвідування](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Маніпуляції](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Область](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Вступ
Babel is a generic multi-purpose compiler for JavaScript. More than that it is a collection of modules that can be used for many different forms of static analysis.
> Static analysis is the process of analyzing code without executing it. (Analysis of code while executing it is known as dynamic analysis). The purpose of static analysis varies greatly. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
You can use Babel to build many different types of tools that can help you be more productive and write better programs.
> ***Ви можете слідкувати за оновленнями в Twitter, підписавшись на [@thejameskyle](https://twitter.com/thejameskyle).***
* * *
# Основи
Babel is a JavaScript compiler, specifically a source-to-source compiler, often called a "transpiler". This means that you give Babel some JavaScript code, Babel modifies the code, and generates the new code back out.
## AST
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
You'll notice that each level of the AST has a similar structure:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Етапи Babel
The three primary stages of Babel are **parse**, **transform**, **generate**.
### Аналіз
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Лексичний аналіз
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Each of the `type`s here have a set of properties describing the token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### Синтаксичний аналіз
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Трансформація
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Генерація
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Обхід
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Відвідувачі
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Шляхи
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Шляхи для Відвідувачів
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### Стан
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Області
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Прив'язки
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Визначення
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Конструктори
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Валідатори
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Перетворювачі
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Написання свого першого плагіну до Babel
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Опреації трансформації
## Відвідування
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Маніпуляції
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Область
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/uk/user-handbook.md
================================================
# Довідник користувача Babel
Цей документ містить всю необхідну інформацію стосовно використання [Babel](https://babeljs.io) та відповідних інструментів.
[](http://creativecommons.org/licenses/by/4.0/)
Цей довідник доступний також іншими мовами, повний список можна знайти у файлі [README](/README.md).
# Зміст
* [Вступ](#toc-introduction)
* [Встановлення Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Запуск Babel CLI в рамках проекту](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Налаштування Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Використання коду згенерованого Babel](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Налаштування Babel (додатково)](#toc-configuring-babel-advanced)
* [Вручну зазначенні плагіни](#toc-manually-specifying-plugins)
* [Налаштування плагінів](#toc-plugin-options)
* [Персоналізація Babel для оточення](#toc-customizing-babel-based-on-environment)
* [Створення власного пресету (preset)](#toc-making-your-own-preset)
* [Babel та інші інструменти](#toc-babel-and-other-tools)
* [Інструменти статичного аналізу](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Документація](#toc-documentation)
* [Фреймворки](#toc-frameworks)
* [React](#toc-react)
* [Текстові редактори та інтегровані середовища розробки (IDE)](#toc-text-editors-and-ides)
* [Підтримка Babel](#toc-babel-support)
* [Форум Babel](#toc-babel-forum)
* [Чат Babel](#toc-babel-chat)
* [Проблеми Babel](#toc-babel-issues)
* [Як створити гарний баг-репорт](#toc-creating-an-awesome-babel-bug-report)
# Вступ
Babel - це загальний багатоцільовий компілятор для JavaScript. Використовуючи Babel, ви можете користуватися (та створювати) наступним поколінням JavaScript, а також наступним поколінням JavaScript інструментів.
JavaScript - це мова, що постійно еволюціонує завдяки новим специфікаціям та пропозиціям, які постійно втілюються в нових версіях мови. Використання Babel дозволить вам скористатися багатьма новими функціями ще до того, як вони стануть доступними усюди.
Babel досягає цього шляхом компілювання JavaScript коду, написанного з використанням останніх стандартів, у версію, яка працюватиме усюди. Цей процес також відомий як source-to-source компіляція або transpiling.
Наприклад, Babel може перетворити синтаксис нової стрілкової функції ES2015 arrow function з цього:
```js
const square = n => n * n;
```
В наступний код:
```js
const square = function square(n) {
return n * n;
};
```
Але Babel може робити набагато більше, ніж це, оскільки Babel має вбудовану підтримку розширень синтаксису (syntax extensions), наприклад JSX синтаксис для React або синтаксис Flow для статичної перевірки типів.
Більш того, все в Babel - це плагін, тому будь-хто може створити свої власні плагіни, використовуючи всю потужність Babel на власний розсуд.
*Навіть більше*, Babel складається з кількох основних модулів, які можуть бути використані будь ким для створення JavaScript інструментів нового покоління.
І багато людей це роблять, наразі екосистема, що зросла навколо Babel, є дуже великою та різноманітною. В цьому посібнику розглянуті як інструменти вбудовані в Babel, так і деякі гарні та дуже корисні інструменти, побудовані спільнотою.
> ***Ви можете слідкувати за оновленнями в Twitter, підписавшись на [@thejameskyle](https://twitter.com/thejameskyle).***
* * *
# Встановлення Babel
У зв'язку з тим, що спільнота JavaScript не має єдиного стандарту по інструменту збирання проекту (build tool), фреймворку (framework), платформі (platform) і так далі, Babel має офіційні інтеграції для всих відомих систем. Будь що, від Gulp до Browserify, від Ember до Meteor, не важливо з чого складається ваш проект - ймовірно, що для нього доступна офіційна версія інтеграції.
В рамках цього посібника ми розглянемо лише вбудовані засоби встановлення Babel, але ви можете також відвідати й інтерактивну [сторінку установки](http://babeljs.io/docs/setup), щоб побачити всі доступні інтеграції.
> **Примітка:** Цей посібник посилається на інструменти командного рядка, такі як `node` та `npm`. Перш ніж продовжити, ви маєте вміти впевнено користуватися цими інструментами.
## `babel-cli`
Babel CLI - це простий спосіб компілювати файли за допомогою Babel з командного рядка.
Давайте спочатку встановимо його глобально, для того, щоб навчитися основам.
```sh
$ npm install --global babel-cli
```
Тепер ми можемо скомпілювати наш перший файл наступним чином:
```sh
$ babel my-file.js
```
Ця команда поверне скомпільований результат безпосередньо у вікно терміналу. Для того, щоб спрямувати його у файл, ми маємо викликати команду з ключем `--out-file` або `-o`.
```sh
$ babel example.js --out-file compiled.js
# або
$ babel example.js -o compiled.js
```
Якщо ми бажаємо скомпілювати всі файли в теці та покласти результати у нову теку, то маємо скористатися наступними ключами командного рядка: `--out-dir` або `-d`.
```sh
$ babel src --out-dir lib
# або
$ babel src -d lib
```
### Запуск Babel CLI в рамках проекту
Хоч ви і *можете* встановити Babel CLI глобально на вашому комп'ютері, все ж набагато краще встановлювати його **локально** для кожного проекту.
Для цього є дві основні причини.
1. Різні проекти на одному комп'ютері можуть залежати від різних версій Babel, це дозволить вам оновлювати кожен проект окремо.
2. Це означає, що у вас немає неявної залежності від середовища, в якому ви працюєте. Це робить ваш проект набагато більш портативним та простішим в налаштуванні.
Ми можемо встановити Babel CLI локально, виконавши наступну команду:
```sh
$ npm install --save-dev babel-cli
```
> **Примітка:** Оскільки, як правило, запускати Babel глобально - це погана ідея, ви можете видалити глобальну копію, виконавши команду:
>
> ```sh
$ npm uninstall --global babel-cli
```
Після завершення інсталяції файл `package.json` повинен виглядати наступним чином:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Тепер, замість запуску Babel безпосередньо з командного рядка, ми додамо наші команди в **npm scripts**, які будуть використовувати нашу локальну версію.
Просто додайте поле `"scripts"` до вашого файлу `package.json` та додайте туди команди babel під ключем `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Тепер ми можемо викликати наступну команду з нашого терміналу:
```js
npm run build
```
Ця команда запустить Babel так само, як і раніше, але тепер з використанням локальної копії Babel.
## `babel-register`
Ще одним поширеним методом запуску Babel є `babel-register`. Цей параметр дозволяє запускати Babel лише за допомогою включення необхідних файлів, що може зробити інтеграцію простішою.
Зверніть увагу, що це не підходить для використання на "бойовому" сервері. Зазвичай вважається поганою практикою розгортати код, скомпільований таким чином. Набагато краще скомпілювати весь код до того, як він стане доступний кінцевому користувачу. Однак цей підход чудово працює для збирання скриптів або інших інструментів, які ви запускаєте локально.
Для початку давайте створимо файл `index.js` в нашому проекті.
```js
console.log("Hello world!");
```
Якщо ми запустимо цей файл, виконавши `node index.js` він не буде скомпільований за допомогою Babel. Тому замість безпосереднього виконання ми налаштуємо `babel-register`.
Спочатку встановимо `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Наступним кроком необхідно створити файл `register.js` в нашому проекті з наступним кодом всередині:
```js
require("babel-register");
require("./index.js");
```
Цей файл *реєструє * Babel в якості модуля Node та починає компілювати кожен `require`'d файл.
Тепер замість того, щоб запускати наш код за допомогою `node index.js`, ми можемо скористатися файлом `register.js`.
```sh
$ node register.js
```
> **Примітка:** Ви не можете зареєструвати Babel в тому ж самому файлі, який ви хочете скомпілювати. Це пов'язано з тим, що node виконує цей файл до того, як Babel може його скомпілювати.
>
> ```js
require("babel-register");
// не скомпілює:
console.log("Hello world!");
```
## `babel-node`
Якщо ви просто запускаєте деякий код за допомогою `node` CLI, то найпростішим способом інтегрувати Babel буде використання `babel-node` CLI, який по великому рахунку просто заміняє `node` CLI.
Зверніть увагу, що це не підходить для використання на "бойовому" сервері. Зазвичай вважається поганою практикою розгортати код, скомпільований таким чином. Набагато краще скомпілювати весь код до того, як він стане доступний кінцевому користувачу. Однак цей підход чудово працює для збирання скриптів або інших інструментів, які ви запускаєте локально.
Спочатку переконайтеся, що `babel-cli` встановлений.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Потім потрібно всюди замінити виклик `node` на `babel-node`.
Якщо ви використовуєте npm `scripts`, то ви можете просто зробити:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
В іншому випадку ви повинні будете прописати шлях до самого `babel-node`.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Порада: Ви також можете використати [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
Якщо з деякої причини вам потрібно використовувати Babel програмно, ви можете використовувати безпосередньо сам пакет `babel-core`.
Спочатку встановіть `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
Якщо у вас є рядок з JavaScript кодом, то ви можете скомпілювати його безпосередньо, використовуючи `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
Якщо ви працюєте з файлами, то можете використовувати або асинхронний api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Або синхронний api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
Якщо з тої чи іншої причини у вас вже є Babel AST, то ви може перетворити безпосередньо з AST.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Налаштування Babel
Можливо, ви вже помітили, що, на перший погляд, запуск Babel сам по собі нічого не робить, окрім копіювання JavaScript файлів з одного місця до іншого.
Так відбувається тому, що ми не сказали Babel зробити що-небудь ще.
> Оскільки Babel - це компілятор загального призначення, який може використовуватися в безліч різних способів, то він не робить нічого за промовчанням. Ви маєте явно сказати Babel те, що він повинен робити.
Ви можете дати Babel інструкції про те, що він має робити, встановивиши плагіни - **plugins** або пресети - **presets** (групи плагінів).
## `.babelrc`
Перш ніж ми почнемо казати Babel, що робити, нам потрібно створити файл конфігурації. Все, що вам потрібно зробити, це створити файл `.babelrc` у корені вашого проекту. Заповнимо його наступним чином:
```js
{
"presets": [],
"plugins": []
}
```
Цей файл містить налаштування Babel і змушує його робити те, що ви хочете.
> **Примітка:** Хоча ви й можете передаватити параметри Babel іншим чином, використання файлу `.babelrc` - це звичний і найкращий спосіб.
## `babel-preset-es2015`
Давайте скажемо Babel скомпілювати ES2015 (найновішу версію стандарту JavaScript, також відому як ES6) в ES5 (версія, що зараз доступна в більшості JavaScript середовищ).
Встановимо для цього пресет Babel "es2015":
```sh
$ npm install --save-dev babel-preset-es2015
```
Далі змінимо наш файл `.babelrc` щоб включити цей пресет.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Налаштувати React так само легко. Просто встановіть пресет:
```sh
$ npm install --save-dev babel-preset-react
```
Потім додайте пресет до вашого файлу `.babelrc`:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript має деякі пропозиції, які вносяться до стандарту через процес TC39 (технічний комітет по стандарту ECMAScript).
Цей процес розбитий на 5 етапів (0-4). По мірі того, як пропозиції набирають підтримку та стають майже готовими для прийняття до стандарту, вони проходять через різні етапи і нарешті приймаються у стандарт на стадії 4.
Ці етапи включені до Babel як 4 різних пресети (presets):
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Зверніть увагу, що немає пресету stage-4, оскільки він просто є згаданим вище пресетом `es2015`.
Кожен з цих пресетів вимагає пресет для більш пізніх етапів. Тобто `babel-preset-stage-1` вимагає `babel-preset-stage2`, який вимагає `babel-preset-stage-3`.
Ви можете встановити необхідний етап виконавши:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Потім ви можете додати його до вашого файлу конфігурації `.babelrc`.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Використання коду згенерованого Babel
Отже, ви скомпілювали ваш код за допомогою Babel, але це ще не кінець історії.
## `babel-polyfill`
Майже увесь футуристичний синтаксис JavaScript можна скомпілювати за допомогою Babel, але для API це не завжди так.
Наприклад, наступний код містить стрілкову функцію (arrow function):
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Після компіляції він перетворюється на це:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
Але цей код може і не виконатися тому, що `Array.from` може існувати не в кожному JavaScript середовищі.
Uncaught TypeError: Array.from is not a function
Щоб вирішити цю проблему, ми скористаємося дечим під назвою [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Кажучи простими словами, polyfill - це шматок коду, який дублює native api, котрого бракує в поточний час виконання. Це дозволяє користуватися такими API як `Array.from` ще до того, як вони стають доступними.
Babel має неперевершений [core-js](https://github.com/zloirock/core-js) в якості polyfill, а також модифікований [regenerator](https://github.com/facebook/regenerator) runtime для отримання генераторів та можливості виклику асинхронних функцій.
Щоб включити Babel polyfill, спочатку встановимо його за допомогою npm:
```sh
$ npm install --save babel-polyfill
```
А потім включаємо polyfill у верхній частині будь-якого файлу, що його використовує:
```js
import "babel-polyfill";
```
## `babel-runtime`
Для реалізації деталей специфікації ECMAScript, Babel буде використовувати "helper" методи для того, щоб тримати згенерований код чистим.
Оскільки ці допоміжні методи можуть бути досить великими, а також вони додаються до верхньої частини всіх файлів, ви можете перемістити їх усі в один "runtime", який потім викликається.
Почніть з інсталяції `babel-plugin-transform-runtime` та `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Потім відредагуйте ваш `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Тепер Babel буде компілювати даний код:
```js
class Foo {
method() {}
}
```
В оце:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Замість додавання допоміжних `_classCallCheck` та `_createClass` до кожного файлу, де вони потрібні.
* * *
# Налаштування Babel (додатково)
Більшість людей може користуватися лише вбудованими до Babel пресетами, але Babel має набагато більше потужності.
## Вручну зазначенні плагіни
Пресети Babel - це просто колекції попередньо сконфігурованих плагінів (plugins), якщо ви хочете зробити щось по-іншому, то маєте вказати плагіни вручну. Це робиться майже так само, як і при роботі з пресетами.
Спочатку встановіть плагін:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Потім додайте поле ` plugins ` до вашого `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
Це дає вам набагато більше контролю над конкретними перетвореннями.
Повний перелік офіційних плагінів знаходиться на [сторінці Babel Plugins](http://babeljs.io/docs/plugins/).
Гляньте також і на інші плагіни, які були [побудовані спільнотою](https://www.npmjs.com/search?q=babel-plugin). Якщо ви хочете дізнатися, як написати свій плагін, читайте [Довідник розробника плагінів](plugin-handbook.md).
## Налаштування плагінів
Багато плагінів також мають параметри для налаштування їхньої поведінки. Наприклад, багато перетворень мають вільний "loose" режим, який додає деяку специфічну поведінку для генерації більш простого та продуктивного коду.
Для того, щоб додати параметри до плагіна, просто зробіть наступні зміни:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> Я ще працюю над оновленнями до документації по плагінах, щоб освітити кожен параметр найближчим часом. [Слідкуйте за моїми оновленнями](https://twitter.com/thejameskyle).
## Персоналізація Babel для оточення
Плагіни Babel вирішують багато різних завдань. Багато з них є інструментами для розробки, що можуть допомогти вам в налагодженні вашого коду або інтеграції з іншими інструментами. Є також багато плагінів, які призначені для оптимізації коду на "бойовому" сервері.
З цієї причини стало загальною практикою створювати різні налаштування Babel в залежності від середовища. Ви можете легко налаштувати це у вашому файлі `.babelrc`.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel буде використовувати необхідну конфігурацію всередині `env` в залежності від поточного середовища.
Поточне середовище буде використовувати `process.env.BABEL_ENV`. Коли `BABEL_ENV` недоступна, то буде використана `NODE_ENV`, а якщо і вона недоступна - за промовчанням буде використана `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Примітка:** `[COMMAND]` - це будь-яка команда для запуску Babel (як то `babel`, `babel-node` чи просто `node` якщо ви використовуєте register hook).
>
> **Порада:** Для того, щоб команди працювали як на unix так і на windows платформі - скористайтеся [`cross-env`](https://www.npmjs.com/package/cross-env).
## Створення власного пресету (preset)
Обрати плагіни вручну? Налаштування плагінів? Налаштування в залежності від середовища? Всі ці налаштування можуть стати цілою купою повторень для всіх ваших проектів.
З цієї причини ми закликаємо спільноту створювати свої власні пресети. Це може бути пресет для конкретної [версії node](https://github.com/leebenson/babel-preset-node5) з якою ви працюєте, або це може бути пресет для [усієї](https://github.com/cloudflare/babel-preset-cf) вашої [компанії](https://github.com/airbnb/babel-preset-airbnb).
Пресет створити дуже легко. Скажімо у вас є цей файл `.babelrc`:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
Все, що вам потрібно зробити - це створити новий проект, користуючись наступним іменуванням `babel-preset-*` (будь ласка, будьте відповідальним за цей простір імен) і створіть два файли.
Спочатку створіть новий файл `package.json` з необхідним ключем `dependencies` для вашого пресету.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Потім створіть файл `index.js`, який експортує вміст вашого файлу `.babelrc`, замінивши плагін/преcет на виклики `require`.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Потім просто опублікуйте цей пресет в npm, і можете користуватися ним як будь-яким іншим пресетом.
* * *
# Babel та інші інструменти
Babel досить простий в налаштуванні коли ви з ним трохи розберетеся. Але все ж вам може бути досить важко розібратися, як його налаштувати за допомогою інших інструментів. Тим не менш, ми намагаємося працювати в тісному контакті з іншими проектами для того, щоб зробити взаємодію якомога простішою.
## Інструменти статичного аналізу
Новітні стандарти принесли багато нового синтаксису в мову, і інструменти статичного аналізу вже починають розуміти цей синтаксис.
### Linting
Одним з найбільш популярних інструментів для перевірки коректності синтаксису та стилю (linting) є [ESLint](http://eslint.org). У зв'язку з цим ми підтримуємо офіційну інтеграцію [`babel-eslint`](https://github.com/babel/babel-eslint).
Спочатку встановіть `eslint` та `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Далі створіть або використайте існуючий файл `.eslintrc` у вашому проекті і вкажіть в ключі `parser` значення `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Тепер додайте завдання `lint` всередині ключа scripts до вашого npm `package.json`:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Потім просто запустіть завдання, ось і всі налаштування.
```sh
$ npm run lint
```
Для отримання додаткової інформації відвідайте розділи документації [`babel-eslint`](https://github.com/babel/babel-eslint) або [`eslint`](http://eslint.org).
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS - надзвичайно популярний інструмент для того, щоб підняти linting на вищий рівень і навчити його перевіряти безпосередньо стиль коду. Ключовий супровідник обох - Babel і JSCS проектів ([@hzoo](https://github.com/hzoo)) підтримує офіційну інтеграцію з JSCS.
Навіть краще, ця інтеграція зараз живе в JSCS, сама по собі як параметр `--esnext`. Тому аби інтегрувати Babel, достатньо лише виконати:
$ jscs . --esnext
З командного рядка, або можете додати параметр `esnext` до вашого файлу `.jscsrc`.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
Для отримання додаткової інформації відвідайте розділи документації [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) або [`jscs`](http://jscs.info).
### Документація
Використовуючи Babel, ES2015 та Flow ви зможете краще пізнати свій код. А за допомогою [documentation.js](http://documentation.js.org) ви зможете з легкістю генерувати детальну документацію по API.
Documentation.js використовує Babel за лаштунками, щоб підтримувати увесь останній синтаксис, включаючи анотації Flow для того, щоб описати типи у вашому коді.
## Фреймворки
Всі основні JavaScript фреймворки зараз зосереджені на підгонці своїх API навколо майбутнього мови. У зв'язку з цим було зроблено багато роботи.
Фреймворки мають можливість не тільки використовувати Babel, але й розширюють його таким чином, щоб поліпшити взаємодію з користувачем.
### React
React кардинально змінили своє API для того щоб підігнати його під класи ES2015 ([Читайте про оновлений API тут](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Навіть більше, React спирається на Babel, щоб компілювати свій JSX синтаксис, і оголосили застарілими (deprecated) свої власні інструменти на користь Babel. Ви можете почати, налаштувавши пакет `babel-preset-react` у відповідності до [наведених вище інструкцій](#babel-preset-react).
Спільнота React одразу підтримала Babel. В даний час уже є певна кількість трансформацій [побудованих спільнотою](https://www.npmjs.com/search?q=babel-plugin+react).
Одним з найцікавіших є плагін [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform), який у поєднанні з низкою [специфічних для React трансформацій](https://github.com/gaearon/babel-plugin-react-transform#transforms) може робити речі накшталт *перезавантаження модулів на льоту (hot module reloading)*, а також інші засоби налагодження.
## Текстові редактори та інтегровані середовища розробки (IDE)
Вивчення ES2015, JSX та синтаксису Flow разом з Babel може бути корисним, але якщо ваш текстовий редактор всього цього не підтримує, то це не сприяє отриманню гарного досвіду. З цієї причини, вам можливо доведеться встановити текстовий редактор або IDE з плагіном Babel.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Підтримка Babel
У Babel дуже велика і швидко зростаюча спільнота. Оскільки ми ростемо, ми хочемо, щоб люди мали всі ресурси для досягнення успіху. Саме тому ми пропонуємо низку каналів для отримання підтримки.
Пам'ятайте, що в усіх цих спільнотах слід дотримуватися [Кодексу Поведінки (Code of Conduct)](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). Якщо ви порушите Правила Поведінки, будуть вжиті відповідні заходи. Тож, будь ласка, прочитайте ці правила та дотримуйтеся їх при взаємодії з іншими учасниками.
Ми також плануємо зосередитись на збільшенні спільноти підтримки, для людей, які можуть прийти на допомогу іншим. Якщо хтось задає питання, на яке ви знаєте відповідь, будь-ласка, виділіть кілька хвилин і домоможіть їм. Зробіть все, що у ваших силах аби бути добрим і ставтеся до питання з розумінням.
## Форум Babel
[Discourse](http://www.discourse.org) безкоштовно надали нам своє програмне забезпечення для створення форуму (і ми їх любимо за це!). Якщо форуми - це ваша стихія, будь ласка, відвідайте [discuss.babeljs.io](https://discuss.babeljs.io).
## Чат Babel
Усі люблять [Slack](https://slack.com). Якщо ви шукаєте невідкладної підтримки з боку спільноти, то відвідайте наш чат на [slack.babeljs.io](https://slack.babeljs.io).
## Проблеми Babel
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
Якщо ви виявили проблему і хочете відкрити для цього нову тему:
* [Пошукайте вже існуючу тему](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Як створити гарний баг-репорт
Проблеми Babel інколи дуже важко відлагодити віддалено, то ж ми потребуємо будь-якої допомоги, яку ви зможете надати. Кілька додаткових хвилин, присвячених створенню дійсно гарного баг репорту зможуть допомогти вирішенню вашої проблеми у значно швидший термін.
По-перше, спробуйте ізолювати неполадку. Вкрай малоймовірно, що кожна частина вашої конфігурації провокує проблему. Якщо ваша проблема - це частина вхідного коду, спробуйте видалити стільки коду, наскільки це можливо до тих пір поки проблема буде існувати.
> [WIP]
* * *
> ***Ви можете слідкувати за оновленнями в Twitter, підписавшись на [@thejameskyle](https://twitter.com/thejameskyle).***
================================================
FILE: translations/vi/README.md
================================================
# Babel Handbook
This handbook is divided into two parts:
* [User Handbook](user-handbook.md) - How to setup/configure Babel and more.
* [Plugin Handbook](plugin-handbook.md) - How to create plugins for Babel.
> For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.
If you are reading a non-english translation of this handbook you may still find english sections that have not yet been translated. If you would like to contribute to one of the translations you must do so through Crowdin. Please read the [contributing guidelines](/CONTRIBUTING.md) for more information. You will find a number of english words that are programming concepts. If these were translated to other languages there would be a lack of consistency and fluency when reading about them. In many cases you will find the literal translation followed by the english term in parenthesis `()`. For example: Abstract Syntax Trees (ASTs).
================================================
FILE: translations/vi/plugin-handbook.md
================================================
# Babel Plugin Handbook
Tài liệu này hướng dẫn làm cách nào để tạo ra [Babel](https://babeljs.io) [plugins](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
Tài liệu này hỗ trợ nhiều ngôn ngữ khác nhau, xem file [README](/README.md) để biết thêm chi tiết.
# Mục lục
* [Giới thiệu](#toc-introduction)
* [Cở bản](#toc-basics)
* [Cây AST](#toc-asts)
* [Quy trình của Babel](#toc-stages-of-babel)
* [Bộ Parse](#toc-parse)
* [Lexical Analysis](#toc-lexical-analysis)
* [Syntactic Analysis](#toc-syntactic-analysis)
* [Transform](#toc-transform)
* [Generate](#toc-generate)
* [Traversal](#toc-traversal)
* [Visitors](#toc-visitors)
* [Paths](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [State](#toc-state)
* [Scopes](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions](#toc-definitions)
* [Builders](#toc-builders)
* [Validators](#toc-validators)
* [Converters](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [Bắt đầu viết Babel Plugin](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# Giới thiệu
Babel is a generic multi-purpose compiler for JavaScript. More than that it is a collection of modules that can be used for many different forms of static analysis.
> Static analysis is the process of analyzing code without executing it. (Analysis of code while executing it is known as dynamic analysis). The purpose of static analysis varies greatly. It can be used for linting, compiling, code highlighting, code transformation, optimization, minification, and much more.
You can use Babel to build many different types of tools that can help you be more productive and write better programs.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Cở bản
Babel là 1 trình biên dịch (compiler) dành cho Javascript, đặt biệt là nó là trình biên dịch source-to-source và thường gọi là "transpiler". Có nghĩa là code Javascript của bạn sẽ được Babel xử lí và cho ra 1 code mới.
## Cây AST
Each of these steps involve creating or working with an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or AST.
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> Check out [AST Explorer](http://astexplorer.net/) to get a better sense of the AST nodes. [Here](http://astexplorer.net/#/Z1exs6BWMq) is a link to it with the example code above pasted in.
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
Or as a JavaScript Object like this:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
Bạn có thể thấy mỗi một cấp của cây AST có cấu trúc tương đồng nhau:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> Note: Some properties have been removed for simplicity.
Each of these are known as a **Node**. An AST can be made up of a single Node, or hundreds if not thousands of Nodes. Together they are able to describe the syntax of a program that can be used for static analysis.
Every Node has this interface:
```typescript
interface Node {
type: string;
}
```
The `type` field is a string representing the type of Node the object is (ie. `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Quy trình của Babel
Có 3 bước chính trong quá trình biên dịch của Babel: **parse**, **transform**, **generate**.
### Bộ Parse
Ở bước **parse**, đầu vào là code và đầu ra là cây AST. Có 2 quá trình của quá trình parse: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) và [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexical Analysis sẽ nhận vào là code dưới dạng là chuỗi (string) và chuyển hoá nó thành tập hợp các **token**.
Có thể hiểu token như mảng 1 chiều chứa các cú pháp của ngôn ngữ.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
Mỗi thuộc tính `type` chứa các thuộc tính mô tả token:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Cũng giống node trong cây AST, các token cũng có các thuộc tính `start`, `end`, and `loc`.
#### Syntactic Analysis
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### Transform
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### Generate
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## Traversal
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### Visitors
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### Scopes
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### Validators
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# Bắt đầu viết Babel Plugin
Tới thời điểm này chắc bạn đã quen với một số khái niệm cơ bản về Babel. Vì vậy, hãy cùng ráp mọi thứ lại với nhau sử dụng các plugin API để tạo ra 1 babel plugin đơn giản.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# Plugin Options
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# Building Nodes
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/vi/user-handbook.md
================================================
# Babel User Handbook
This document covers everything you ever wanted to know about using [Babel](https://babeljs.io) and related tooling.
[](http://creativecommons.org/licenses/by/4.0/)
Tài liệu này hỗ trợ nhiều ngôn ngữ khác nhau, xem file [README](/README.md) để biết thêm chi tiết.
# Mục lục
* [Giới thiệu](#toc-introduction)
* [Setting up Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [Configuring Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [Executing Babel-generated code](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Configuring Babel (Advanced)](#toc-configuring-babel-advanced)
* [Manually specifying plugins](#toc-manually-specifying-plugins)
* [Plugin options](#toc-plugin-options)
* [Customizing Babel based on environment](#toc-customizing-babel-based-on-environment)
* [Making your own preset](#toc-making-your-own-preset)
* [Babel and other tools](#toc-babel-and-other-tools)
* [Static analysis tools](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [Documentation](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [Text Editors and IDEs](#toc-text-editors-and-ides)
* [Babel Support](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# Giới thiệu
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can use (and create) the next generation of JavaScript, as well as the next generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
```js
const square = n => n * n;
```
Into the following:
```js
const square = function square(n) {
return n * n;
};
```
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
*Even further* than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I'll be covering both how built-in Babel tools work as well as some useful things from around the community.
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
* * *
# Setting up Babel
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we're just going to cover the built-in ways of setting up Babel, but you can also visit the interactive [setup page](http://babeljs.io/docs/setup) for all of the integrations.
> **Note:** This guide is going to refer to command line tools like `node` and `npm`. Before continuing any further you should be comfortable with these tools.
## `babel-cli`
Babel's CLI is a simple way to compile files with Babel from the command line.
Let's first install it globally to learn the basics.
```sh
$ npm install --global babel-cli
```
We can compile our first file like so:
```sh
$ babel my-file.js
```
This will dump the compiled output directly into your terminal. To write it to a file we'll specify an `--out-file` or `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
If we want to compile a whole directory into a new directory we can do so using `--out-dir` or `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### Running Babel CLI from within a project
While you *can* install Babel CLI globally on your machine, it's much better to install it **locally** project by project.
There are two primary reasons for this.
1. Different projects on the same machine can depend on different versions of Babel allowing you to update one at a time.
2. It means you do not have an implicit dependency on the environment you are working in. Making your project far more portable and easier to setup.
We can install Babel CLI locally by running:
```sh
$ npm install --save-dev babel-cli
```
> **Note:** Since it's generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
>
> ```sh
$ npm uninstall --global babel-cli
```
After that finishes installing, your `package.json` file should look like this:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now instead of running Babel directly from the command line we're going to put our commands in **npm scripts** which will use our local version.
Simply add a `"scripts"` field to your `package.json` and put the babel command inside there as `build`.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
Now from our terminal we can run:
```js
npm run build
```
This will run Babel the same way as before, only now we are using a local copy.
## `babel-register`
The next most common method of running Babel is through `babel-register`. This option will allow you to run Babel just by requiring files, which may integrate with your setup better.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let's create an `index.js` file in our project.
```js
console.log("Hello world!");
```
If we were to run this with `node index.js` this wouldn't be compiled with Babel. So instead of doing that, we'll setup `babel-register`.
First install `babel-register`.
```sh
$ npm install --save-dev babel-register
```
Next, create a `register.js` file in the project and write the following code:
```js
require("babel-register");
require("./index.js");
```
What this does is *registers* Babel in Node's module system and begins compiling every file that is `require`'d.
Now, instead of running `node index.js` we can use `register.js` instead.
```sh
$ node register.js
```
> **Note:** You can't register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
If you are just running some code via the `node` CLI the easiest way to integrate Babel might be to use the `babel-node` CLI which largely is just a drop in replacement for the `node` CLI.
Note that this is not meant for production use. It's considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have `babel-cli` installed.
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
Then replace wherever you are running `node` with `babel-node`.
If you are using npm `scripts` you can simply do:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
Otherwise you'll need to write out the path to `babel-node` itself.
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> Tip: You can also use [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
If you need to use Babel programmatically for some reason, you can use the `babel-core` package itself.
First install `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
If you have a string of JavaScript you can compile it directly using `babel.transform`.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
If you are working with files you can use either the asynchronous api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
Or the synchronous api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
If you already have a Babel AST for whatever reason you may transform from the AST directly.
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# Configuring Babel
You may have noticed by now that running Babel on its own doesn't seem to do anything other than copy JavaScript files from one location to another.
This is because we haven't told Babel to do anything yet.
> Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn't do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing **plugins** or **presets** (groups of plugins).
## `.babelrc`
Before we start telling Babel what to do. We need to create a configuration file. All you need to do is create a `.babelrc` file at the root of your project. Start off with it like this:
```js
{
"presets": [],
"plugins": []
}
```
This file is how you configure Babel to do what you want.
> **Note:** While you can also pass options to Babel in other ways the `.babelrc` file is convention and is the best way.
## `babel-preset-es2015`
Let's start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We'll do this by installing the "es2015" Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
Next we'll modify our `.babelrc` to include that preset.
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
Setting up React is just as easy. Just install the preset:
```sh
$ npm install --save-dev babel-preset-react
```
Then add the preset to your `.babelrc` file:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript also has some proposals that are making their way into the standard through the TC39's (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> Note that there is no stage-4 preset as it is simply the `es2015` preset above.
Each of these presets requires the preset for the later stages. i.e. `babel-preset-stage-1` requires `babel-preset-stage-2` which requires `babel-preset-stage-3`.
Simply install the stage you are interested in using:
```sh
$ npm install --save-dev babel-preset-stage-2
```
Then you can add it to your `.babelrc` config.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# Executing Babel-generated code
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Configuring Babel (Advanced)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## Manually specifying plugins
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## Plugin options
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## Customizing Babel based on environment
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## Making your own preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# Babel and other tools
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## Static analysis tools
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### Documentation
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## Text Editors and IDEs
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel Support
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) on Twitter.***
================================================
FILE: translations/zh-Hans/README.md
================================================
# Babel 手册
这本手册分为两个部分:
* [用户手册](user-handbook.md)-如何安装/配置 Babel 及相关内容。
* [插件手册](plugin-handbook.md)-如何为 Babel 创建插件。
> 在 Twitter 上关注 [@thejameskyle](https://twitter.com/thejameskyle),第一时间获取更新。
如果你正在阅读本手册的非英语版本,你或许会发现一些英文的部分还没有被翻译。 如果你想帮助翻译的话你需要通过 Crowdin 来完成。 请先阅读[贡献指导](/CONTRIBUTING.md)来了解这方面更多的信息。 你会发现有些英语单词代表特定的编程概念。 如果按照字面翻译这些单词会导致在阅读时缺乏一致性和流畅性。 在此情形下,字面翻译会写在原文后面的 `()` 内。 例如:抽象语法树(ASTs)
================================================
FILE: translations/zh-Hans/plugin-handbook.md
================================================
# Babel 插件手册
这篇文档涵盖了如何创建 [Babel](https://babeljs.io) [插件](https://babeljs.io/docs/advanced/plugins/)等方面的内容。.
[](http://creativecommons.org/licenses/by/4.0/)
这本手册提供了多种语言的版本,查看 [自述文件](/README.md) 里的完整列表。
# 目录
* [介绍](#toc-introduction)
* [基础](#toc-basics)
* [抽象语法树(ASTs)](#toc-asts)
* [Babel 的处理步骤](#toc-stages-of-babel)
* [解析](#toc-parse)
* [词法分析](#toc-lexical-analysis)
* [语法分析](#toc-syntactic-analysis)
* [转换](#toc-transform)
* [生成](#toc-generate)
* [遍历](#toc-traversal)
* [Visitors(访问者)](#toc-visitors)
* [Paths(路径)](#toc-paths)
* [Paths in Visitors(存在于访问者中的路径)](#toc-paths-in-visitors)
* [State(状态)](#toc-state)
* [Scopes(作用域)](#toc-scopes)
* [Bindings(绑定)](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [Definitions(定义)](#toc-definitions)
* [Builders(构建器)](#toc-builders)
* [Validators(验证器)](#toc-validators)
* [Converters(变换器)](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [编写你的第一个 Babel 插件](#toc-writing-your-first-babel-plugin)
* [转换操作](#toc-transformation-operations)
* [访问](#toc-visiting)
* [获取子节点的Path](#toc-get-the-path-of-a-sub-node)
* [检查节点(Node)类型](#toc-check-if-a-node-is-a-certain-type)
* [检查路径(Path)类型](#toc-check-if-a-path-is-a-certain-type)
* [检查标识符(Identifier)是否被引用](#toc-check-if-an-identifier-is-referenced)
* [找到特定的父路径](#toc-find-a-specific-parent-path)
* [获取同级路径](#toc-get-sibling-paths)
* [停止遍历](#toc-stopping-traversal)
* [处理](#toc-manipulation)
* [替换一个节点](#toc-replacing-a-node)
* [用多节点替换单节点](#toc-replacing-a-node-with-multiple-nodes)
* [用字符串源码替换节点](#toc-replacing-a-node-with-a-source-string)
* [插入兄弟节点](#toc-inserting-a-sibling-node)
* [插入到容器(container)中](#toc-inserting-into-a-container)
* [删除节点](#toc-removing-a-node)
* [替换父节点](#toc-replacing-a-parent)
* [删除父节点](#toc-removing-a-parent)
* [Scope(作用域)](#toc-scope)
* [检查本地变量是否被绑定](#toc-checking-if-a-local-variable-is-bound)
* [生成UID](#toc-generating-a-uid)
* [提升变量声明至父级作用域](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [重命名绑定及其引用](#toc-rename-a-binding-and-its-references)
* [插件选项](#toc-plugin-options)
* [插件的准备和收尾工作](#toc-pre-and-post-in-plugins)
* [在插件中启用其他语法](#toc-enabling-syntax-in-plugins)
* [构建节点](#toc-building-nodes)
* [最佳实践](#toc-best-practices)
* [尽量避免遍历抽象语法树(AST)](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [及时合并访问者对象](#toc-merge-visitors-whenever-possible)
* [可以手动查找就不要遍历](#toc-do-not-traverse-when-manual-lookup-will-do)
* [优化嵌套的访问者对象](#toc-optimizing-nested-visitors)
* [留意嵌套结构](#toc-being-aware-of-nested-structures)
* [单元测试](#toc-unit-testing)
# 介绍
Babel 是一个通用的多功能的 JavaScript 编译器。此外它还拥有众多模块可用于不同形式的静态分析。
> 静态分析是在不需要执行代码的前提下对代码进行分析的处理过程 (执行代码的同时进行代码分析即是动态分析)。 静态分析的目的是多种多样的, 它可用于语法检查,编译,代码高亮,代码转换,优化,压缩等等场景。
你可以使用 Babel 创建多种类型的工具来帮助你更有效率并且写出更好的程序。
> ***在 Twitter 上关注 [@thejameskyle](https://twitter.com/thejameskyle),第一时间获取更新。***
* * *
# 基础
Babel 是 JavaScript 编译器,更确切地说是源码到源码的编译器,通常也叫做“转换编译器(transpiler)”。 意思是说你为 Babel 提供一些 JavaScript 代码,Babel 更改这些代码,然后返回给你新生成的代码。
## 抽象语法树(ASTs)
这个处理过程中的每一步都涉及到创建或是操作[抽象语法树](https://en.wikipedia.org/wiki/Abstract_syntax_tree),亦称 AST。
> Babel 使用一个基于 [ESTree](https://github.com/estree/estree) 并修改过的 AST,它的内核说明文档可以在[这里](https://github. com/babel/babel/blob/master/doc/ast/spec. md)找到。.
```js
function square(n) {
return n * n;
}
```
> [AST Explorer](http://astexplorer.net/) 可以让你对 AST 节点有一个更好的感性认识。 [这里](http://astexplorer.net/#/Z1exs6BWMq)是上述代码的一个示例链接。
这个程序可以被表示成如下的一棵树:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
或是如下所示的 JavaScript Object(对象):
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
你会留意到 AST 的每一层都拥有相同的结构:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> 注意:出于简化的目的移除了某些属性
这样的每一层结构也被叫做 **节点(Node)**。 一个 AST 可以由单一的节点或是成百上千个节点构成。 它们组合在一起可以描述用于静态分析的程序语法。
每一个节点都有如下所示的接口(Interface):
```typescript
interface Node {
type: string;
}
```
字符串形式的 `type` 字段表示节点的类型(如: `"FunctionDeclaration"`,`"Identifier"`,或 `"BinaryExpression"`)。 每一种类型的节点定义了一些附加属性用来进一步描述该节点类型。
Babel 还为每个节点额外生成了一些属性,用于描述该节点在原始代码中的位置。
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
每一个节点都会有 `start`,`end`,`loc` 这几个属性。
## Babel 的处理步骤
Babel 的三个主要处理步骤分别是: **解析(parse)**,**转换(transform)**,**生成(generate)**。.
### 解析
**解析**步骤接收代码并输出 AST。 这个步骤分为两个阶段:[**词法分析(Lexical Analysis) **](https://en.wikipedia.org/wiki/Lexical_analysis)和 [**语法分析(Syntactic Analysis)**](https://en.wikipedia.org/wiki/Parsing)。.
#### 词法分析
词法分析阶段把字符串形式的代码转换为 **令牌(tokens)** 流。.
你可以把令牌看作是一个扁平的语法片段数组:
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
每一个 `type` 有一组属性来描述该令牌:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
和 AST 节点一样它们也有 `start`,`end`,`loc` 属性。.
#### 语法分析
语法分析阶段会把一个令牌流转换成 AST 的形式。 这个阶段会使用令牌中的信息把它们转换成一个 AST 的表述结构,这样更易于后续的操作。
### 转换
[转换](https://en.wikipedia.org/wiki/Program_transformation)步骤接收 AST 并对其进行遍历,在此过程中对节点进行添加、更新及移除等操作。 这是 Babel 或是其他编译器中最复杂的过程 同时也是插件将要介入工作的部分,这将是本手册的主要内容, 因此让我们慢慢来。
### 生成
[代码生成](https://en.wikipedia.org/wiki/Code_generation_(compiler))步骤把最终(经过一系列转换之后)的 AST 转换成字符串形式的代码,同时还会创建[源码映射(source maps)](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/)。.
代码生成其实很简单:深度优先遍历整个 AST,然后构建可以表示转换后代码的字符串。
## 遍历
想要转换 AST 你需要进行递归的[树形遍历](https://en.wikipedia.org/wiki/Tree_traversal)。
比方说我们有一个 `FunctionDeclaration` 类型。它有几个属性:`id`,`params`,和 `body`,每一个都有一些内嵌节点。
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
于是我们从 `FunctionDeclaration` 开始并且我们知道它的内部属性(即:`id`,`params`,`body`),所以我们依次访问每一个属性及它们的子节点。
接着我们来到 `id`,它是一个 `Identifier`。`Identifier` 没有任何子节点属性,所以我们继续。
之后是 `params`,由于它是一个数组节点所以我们访问其中的每一个,它们都是 `Identifier` 类型的单一节点,然后我们继续。
此时我们来到了 `body`,这是一个 `BlockStatement` 并且也有一个 `body`节点,而且也是一个数组节点,我们继续访问其中的每一个。
这里唯一的一个属性是 `ReturnStatement` 节点,它有一个 `argument`,我们访问 `argument` 就找到了 `BinaryExpression`。.
`BinaryExpression` 有一个 `operator`,一个 `left`,和一个 `right`。 Operator 不是一个节点,它只是一个值因此我们不用继续向内遍历,我们只需要访问 `left` 和 `right`。.
Babel 的转换步骤全都是这样的遍历过程。
### Visitors(访问者)
当我们谈及“进入”一个节点,实际上是说我们在**访问**它们, 之所以使用这样的术语是因为有一个[**访问者模式(visitor)**](https://en.wikipedia.org/wiki/Visitor_pattern)的概念。.
访问者是一个用于 AST 遍历的跨语言的模式。 简单的说它们就是一个对象,定义了用于在一个树状结构中获取具体节点的方法。 这么说有些抽象所以让我们来看一个例子。
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// 你也可以先创建一个访问者对象,并在稍后给它添加方法。
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **注意**: `Identifier() { ... }` 是 `Identifier: { enter() { ... } }` 的简写形式。.
这是一个简单的访问者,把它用于遍历中时,每当在树中遇见一个 `Identifier` 的时候会调用 `Identifier()` 方法。
所以在下面的代码中 `Identifier()` 方法会被调用四次(包括 `square` 在内,总共有四个 `Identifier`)。).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
这些调用都发生在**进入**节点时,不过有时候我们也可以在**退出**时调用访问者方法。.
假设我们有一个树状结构:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
当我们向下遍历这颗树的每一个分支时我们最终会走到尽头,于是我们需要往上遍历回去从而获取到下一个节点。 向下遍历这棵树我们**进入**每个节点,向上遍历回去时我们**退出**每个节点。
让我们以上面那棵树为例子走一遍这个过程。
* 进入 `FunctionDeclaration`
* 进入 `Identifier (id)`
* 走到尽头
* 退出 `Identifier (id)`
* 进入 `Identifier (params[0])`
* 走到尽头
* 退出 `Identifier (params[0])`
* 进入 `BlockStatement (body)`
* 进入 `ReturnStatement (body)`
* 进入 `BinaryExpression (argument)`
* 进入 `Identifier (left)`
* 走到尽头
* 退出 `Identifier (left)`
* 进入 `Identifier (right)`
* 走到尽头
* 退出 `Identifier (right)`
* 退出 `BinaryExpression (argument)`
* 退出 `ReturnStatement (body)`
* 退出 `BlockStatement (body)`
* 退出 `FunctionDeclaration`
所以当创建访问者时你实际上有两次机会来访问一个节点。
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
如有必要,你还可以把方法名用`|`分割成`Idenfifier |MemberExpression`形式的字符串,把同一个函数应用到多种访问节点。.
在[flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) 插件中的例子如下:
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
你也可以在访问者中使用别名(如[babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)定义).
例如,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### Paths(路径)
AST 通常会有许多节点,那么节点直接如何相互关联呢? 我们可以使用一个可操作和访问的巨大可变对象表示节点之间的关联关系,或者也可以用**Paths**(路径)来简化这件事情。.
**Path** 是表示两个节点之间连接的对象。
例如,如果有下面这样一个节点及其子节点︰
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
将子节点 `Identifier` 表示为一个路径(Path)的话,看起来是这样的:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
同时它还包含关于该路径的其他元数据:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
当然路径对象还包含添加、更新、移动和删除节点有关的其他很多方法,稍后我们再来看这些方法。
在某种意义上,路径是一个节点在树中的位置以及关于该节点各种信息的响应式 **Reactive** 表示。 当你调用一个修改树的方法后,路径信息也会被更新。 Babel 帮你管理这一切,从而使得节点操作简单,尽可能做到无状态。
#### Paths in Visitors(存在于访问者中的路径)
当你有一个 `Identifier()` 成员方法的访问者时,你实际上是在访问路径而非节点。 通过这种方式,你操作的就是节点的响应式表示(译注:即路径)而非节点本身。
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### State(状态)
状态是抽象语法树AST转换的敌人,状态管理会不断牵扯你的精力,而且几乎所有你对状态的假设,总是会有一些未考虑到的语法最终证明你的假设是错误的。
考虑下列代码:
```js
function square(n) {
return n * n;
}
```
让我们写一个把 `n` 重命名为 `x` 的访问者的快速实现.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
对上面的例子代码这段访问者代码也许能工作,但它很容易被打破:
```js
function square(n) {
return n * n;
}
n;
```
更好的处理方式是使用递归,下面让我们来像克里斯托佛·诺兰的电影盗梦空间那样来把一个访问者放进另外一个访问者里面。
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
当然,这只是一个刻意编写的例子,不过它演示了如何从访问者中消除全局状态。
### Scopes(作用域)
接下来让我们介绍[**作用域(scope)**](https://en.wikipedia.org/wiki/Scope_(computer_science))的概念。 JavaScript 支持[词法作用域](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping),在树状嵌套结构中代码块创建出新的作用域。
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
在 JavaScript 中,每当你创建了一个引用,不管是通过变量(variable)、函数(function)、类型(class)、参数(params)、模块导入(import)还是标签(label)等,它都属于当前作用域。
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
更深的内部作用域代码可以使用外层作用域中的引用。
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
内层作用域也可以创建和外层作用域同名的引用。
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
当编写一个转换时,必须小心作用域。我们得确保在改变代码的各个部分时不会破坏已经存在的代码。
我们在添加一个新的引用时需要确保新增加的引用名字和已有的所有引用不冲突。 或者我们仅仅想找出使用一个变量的所有引用, 我们只想在给定的作用域(Scope)中找出这些引用。
作用域可以被表示为如下形式:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
当你创建一个新的作用域时,需要给出它的路径和父作用域,之后在遍历过程中它会在该作用域内收集所有的引用(“绑定”)。
一旦引用收集完毕,你就可以在作用域(Scopes)上使用各种方法,稍后我们会了解这些方法。
#### Bindings(绑定)
所有引用属于特定的作用域,引用和作用域的这种关系被称作:**绑定(binding)**。.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
单个绑定看起来像这样︰
```js
Text for Translation
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
有了这些信息你就可以查找一个绑定的所有引用,并且知道这是什么类型的绑定(参数,定义等等),查找它所属的作用域,或者拷贝它的标识符。 你甚至可以知道它是不是常量,如果不是,那么是哪个路径修改了它。
在很多情况下,知道一个绑定是否是常量非常有用,最有用的一种情形就是代码压缩时。
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel 实际上是一组模块的集合。本节我们将探索一些主要的模块,解释它们是做什么的以及如何使用它们。
> 注意:本节内容不是详细的 API 文档的替代品,正式的 API 文档将很快提供出来。
## [`babylon`](https://github.com/babel/babylon)
Babylon 是 Babel 的解析器。最初是 从Acorn项目fork出来的。Acorn非常快,易于使用,并且针对非标准特性(以及那些未来的标准特性) 设计了一个基于插件的架构。
首先,让我们安装它。
```sh
$ npm install --save babylon
```
先从解析一个代码字符串开始:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
我们还能像下面这样传递选项给 `parse()`方法:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` 可以是 `"module"` 或者 `"script"`,它表示 Babylon 应该用哪种模式来解析。 `"module"` 将会在严格模式下解析并且允许模块定义,`"script"` 则不会。
> **注意:** `sourceType` 的默认值是 `"script"` 并且在发现 `import` 或 `export` 时产生错误。 使用 `scourceType: "module"` 来避免这些错误。
由于 Babylon 使用了基于插件的架构,因此有一个 `plugins` 选项可以开关内置的插件。 注意 Babylon 尚未对外部插件开放此 API 接口,不排除未来会开放此API。
要查看完整的插件列表,请参见 [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins)文件。.
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
Babel Traverse(遍历)模块维护了整棵树的状态,并且负责替换、移除和添加节点。
运行以下命令安装:
```sh
$ npm install --save babel-traverse
```
我们可以和 Babylon 一起使用来遍历和更新节点:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types模块是一个用于 AST 节点的 Lodash 式工具库(译注:Lodash 是一个 JavaScript 函数工具库,提供了基于函数式编程风格的众多工具函数), 它包含了构造、验证以及变换 AST 节点的方法。 该工具库包含考虑周到的工具方法,对编写处理AST逻辑非常有用。
可以运行以下命令来安装它:
```sh
$ npm install --save babel-types
```
然后按如下所示来使用:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### Definitions(定义)
Babel Types模块拥有每一个单一类型节点的定义,包括节点包含哪些属性,什么是合法值,如何构建节点、遍历节点,以及节点的别名等信息。
单一节点类型的定义形式如下:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### Builders(构建器)
你会注意到上面的 `BinaryExpression` 定义有一个 `builder` 字段。.
```js
builder: ["operator", "left", "right"]
```
这是由于每一个节点类型都有构造器方法builder,按类似下面的方式使用:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
可以创建如下所示的 AST:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
当打印出来之后是这样的:
```js
a * b
```
构造器还会验证自身创建的节点,并在错误使用的情形下会抛出描述性错误,这就引出了下一个方法类型。
### Validators(验证器)
`BinaryExpression` 的定义还包含了节点的字段 `fields` 信息,以及如何验证这些字段。
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
可以创建两种验证方法。第一种是 `isX`。.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
这个测试用来确保节点是一个二进制表达式,另外你也可以传入第二个参数来确保节点包含特定的属性和值。
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
这些方法还有一种断言式的版本,会抛出异常而不是返回 `true` 或 `false`。.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### Converters(变换器)
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator模块是 Babel 的代码生成器,它读取AST并将其转换为代码和源码映射(sourcemaps)。
运行以下命令来安装它:
```sh
$ npm install --save babel-generator
```
然后按如下方式使用:
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
你也可以给 `generate()` 方法传递选项。.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
babel-template 是另一个虽然很小但却非常有用的模块。 它能让你编写字符串形式且带有占位符的代码来代替手动编码, 尤其是生成的大规模 AST的时候。 在计算机科学中,这种能力被称为准引用(quasiquotes)。
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# 编写你的第一个 Babel 插件
现在你已经熟悉了 Babel 的所有基础知识了,让我们把这些知识和插件的 API融合在一起来编写第一个 Babel 插件吧。
先从一个接收了当前`babel`对象作为参数的 [`function`](https://github.com/babel/babel/tree/master/packages/babel-core) 开始。
```js
export default function(babel) {
// plugin contents
}
```
由于你将会经常这样使用,所以直接取出 `babel.types` 会更方便:(译注:这是 ES2015 语法中的对象解构,即 Destructuring)
```js
export default function({ types: t }) {
// plugin contents
}
```
接着返回一个对象,其 `visitor` 属性是这个插件的主要访问者。
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Visitor 中的每个函数接收2个参数:`path` 和 `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
让我们快速编写一个可用的插件来展示一下它是如何工作的。下面是我们的源代码:
```js
foo === bar;
```
其 AST 形式如下:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
我们从添加 `BinaryExpression` 访问者方法开始:
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
然后我们更确切一些,只关注哪些使用了 `===` 的 `BinaryExpression`。
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
现在我们用新的标识符来替换 `left` 属性:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
于是如果我们运行这个插件我们会得到:
```js
sebmck === bar;
```
现在只需要替换 `right` 属性了。
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
这就是我们的最终结果了:
```js
sebmck === dork;
```
完美!我们的第一个 Babel 插件。
* * *
# 转换操作
## 访问
### 获取子节点的Path
为了得到一个AST节点的属性值,我们一般先访问到该节点,然后利用 `path.node.property` 方法即可。
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
如果你想访问到该属性内部的`path`,使用path对象的`get`方法,传递该属性的字符串形式作为参数。
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### 检查节点的类型
如果你想检查节点的类型,最好的方式是:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
你同样可以对节点的属性们做浅层检查:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
功能上等价于:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### 检查路径(Path)类型
一个路径具有相同的方法检查节点的类型:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
就相当于:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### 检查标识符(Identifier)是否被引用
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
或者:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### 找到特定的父路径
有时你需要从一个路径向上遍历语法树,直到满足相应的条件。
对于每一个父路径调用`callback`并将其`NodePath`当作参数,当`callback`返回真值时,则将其`NodePath`返回。.
```js
path.findParent((path) => path.isObjectExpression());
```
如果也需要遍历当前节点:
```js
path.find((path) => path.isObjectExpression());
```
查找最接近的父函数或程序:
```js
path.getFunctionParent();
```
向上遍历语法树,直到找到在列表中的父节点路径
```js
path.getStatementParent();
```
### 获取同级路径
如果一个路径是在一个 `Function`/`Program`中的列表里面,它就有同级节点。
* 使用`path.inList`来判断路径是否有同级节点,
* 使用`path.getSibling(index)`来获得同级路径,
* 使用 `path.key`获取路径所在容器的索引,
* 使用 `path.container`获取路径的容器(包含所有同级节点的数组)
* 使用 `path.listKey`获取容器的key
> 这些API用于 babel-minify >中使用的 transform-merge-sibling-variables >插件.
>
> ```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### 停止遍历
如果你的插件需要在某种情况下不运行,最简单的做法是尽早写回。
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
如果您在顶级路径中进行子遍历,则可以使用2个提供的API方法:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## 处理
### 替换一个节点
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### 用多节点替换单节点
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **注意:>当用多个节点替换一个表达式时,它们必须是 声明。 这是因为Babel在更换节点时广泛使用启发式算法,这意味着您可以做一些非常疯狂的转换,否则将会非常冗长。
>
> ### 用字符串源码替换节点
>
> ```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **注意:>不建议使用这个API,除非您正在处理动态的源码字符串,否则在访问者外部解析代码更有效率。
>
> ### 插入兄弟节点
>
> ```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> 注意:>这里同样应该使用声明或者一个声明数组。 这个使用了在用多个节点替换一个节点>中提到的相同的启发式算法。.
>
> ### 插入到容器(container)中
>
> 如果您想要在AST节点属性中插入一个像` body 0>那样的数组。
它与 insertBefore `/` insertAfter ` 类似, 但您必须指定 ` listKey ` (通常是 ` 正文 `).
>
> ```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### 删除一个节点
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### 替换父节点
只需使用parentPath:` path.parentPath >调用 replaceWith >即可
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
`
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### 删除父节点
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope(作用域)
### 检查本地变量是否被绑定
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
这将遍历范围树并检查特定的绑定。
您也可以检查一个作用域是否有**自己的>绑定:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### 创建一个 UID
这将生成一个标识符,不会与任何本地定义的变量相冲突。
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### 提升变量声明至父级作用域
有时你可能想要推送一个` VariableDeclaration >,这样你就可以分配给它。
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
`
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### 重命名绑定及其引用
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
或者,您可以将绑定重命名为生成的唯一标识符:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# 插件选项
如果您想让您的用户自定义您的Babel插件的行为您可以接受用户可以指定的插件特定选项,如下所示:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
这些选项会通过`状态>对象传递给插件访问者:
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
`
这些选项是特定于插件的,您不能访问其他插件中的选项。
## 插件的准备和收尾工作
插件可以具有在插件之前或之后运行的函数。它们可以用于设置或清理/分析目的。
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## 在插件中启用其他语法
插件可以启用babylon plugins>,以便用户不需要安装/启用它们。 这可以防止解析错误,而不会继承语法插件。
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## 抛出一个语法错误
如果您想用babel-code-frame和一个消息抛出一个错误:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
该错误看起来像:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# 构建节点
编写转换时,通常需要构建一些要插入的节点进入AST。 如前所述,您可以使用` babel-types >包中的builder >方法。
构建器的方法名称就是您想要的节点类型的名称,除了第一个字母小写。 例如,如果您想建立一个 MemberExpression >您可以使用 t.memberExpression(...)>.
这些构建器的参数由节点定义决定。 有一些正在做的工作,以生成易于阅读的文件定义,但现在他们都可以在此处找到。.
节点定义如下所示:
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
`
在这里你可以看到关于这个特定节点类型的所有信息,包括如何构建它,遍历它,并验证它。
通过查看 ` 生成器 ` 属性, 可以看到调用生成器方法所需的3个参数 (` t. 情况 `).
```js
生成器: ["object", "property", "computed"],
```
> 请注意,有时在节点上可以定制的属性比``构建器>数组包含的属性更多。 这是为了防止生成器有太多的参数。 在这些情况下,您需要手动设置属性。 一个例子是 ClassMethod >.
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
``
>
> You can see the validation for the builder arguments with the `fields` object.
>
> ```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# 最佳实践
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## 尽量避免遍历抽象语法树(AST)
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### 及时合并访问者对象
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### 可以手动查找就不要遍历
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## 优化嵌套的访问者对象
当您嵌套访问者(visitor)时,把它们嵌套在您的代码中可能是有意义的。
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
但是,每当调用`FunctionDeclaration()>时都会创建一个新的访问者对象。 That can be costly, because Babel does some processing each time a new
visitor object is passed in (such as exploding keys containing multiple types,
performing validation, and adjusting the object structure). Because Babel stores
flags on visitor objects indicating that it's already performed that processing,
it's better to store the visitor in a variable and pass the same object each
time.
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
`
如果您在嵌套的访问者中需要一些状态,像这样:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
您可以将它作为状态传递给`traverse() 0>方法,并有权访问this`在访问者中。
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## 留意嵌套结构
有时候在考虑给定的转换时,可能会忘记给定的转换结构可以是嵌套的。
例如,想象一下,我们想要查找`构造函数` ` ClassMethod ` ` Foo ` ` ClassDeclaration `.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
我们忽略了类可以嵌套的事实,使用遍历的话,上面我们也会得到一个嵌套的`构造函数>:
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
`
## 单元测试
有几种主要的方法来测试babel插件:快照测试,AST测试和执行测试。 对于这个例子,我们将使用 jest >,因为它支持盒外快照测试。 我们在这里创建的示例是托管在这个 repo>.
首先我们需要一个babel插件,我们将把它放在src / index.js中。
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### 快照测试
接下来,用`` npm install --save-dev babel-core jest >安装我们的依赖关系,
那么我们可以开始写我们的第一个测试:快照。 快照测试允许我们直观地检查我们的babel插件的输出。 我们给它一个输入,告诉它一个快照,并将其保存到一个文件。 我们检查快照到git中。 这允许我们来看看我们什么时候影响了我们任何试用例子测试的输出。 它也给出了使用差异在拉请求的时候。 当然,您可以用任何测试框架来做到这一点,但是要更新一下快照就像jest -u >一样简单.
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
``
这给了我们一个快照文件在`` src / __ tests __ / __ snapshots __ / index-test.js.snap >.
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
``
如果我们在插件中将“bar”更改为“baz”并再次运行,则可以得到以下结果:
```diff
接收到的值与存储的快照1不匹配。
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
我们看到我们对插件代码的改变如何影响了我们插件的输出 如果输出看起来不错,我们可以运行`jest -u >来更新快照。
AST 测试
除了快照测试外,我们还可以手动检查AST。 这是一个简单但是脆弱的例子。 对于更多涉及的情况,您可能希望利用Babel-遍历。 它允许您用访问者>键指定一个对象,就像您使用插件本身。
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
`
### Exec Tests
在这里,我们将转换代码,然后评估它的行为是否正确。 请注意,我们在测试中没有使用``assert>。 这确保如果我们的插件做了奇怪的操作,如意外删除断言线,但测试仍然失败。
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
``
Babel核心使用类似的方法>去获取快照和执行测试。
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
这个包使测试插件更容易。 如果您熟悉ESLint的[ RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests)您应该对这是熟悉的。 您可以看看[the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md)去充分了解可能的情况,但这里有一个简单的例子:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***对于将来的更新,请跟随 @thejameskyle >和 @babeljs > 的Twitter。
================================================
FILE: translations/zh-Hans/user-handbook.md
================================================
# Babel 用户手册
这本手册涵盖了关于 [Babel](https://babeljs.io) 的使用及其相关工具的内容。
[](http://creativecommons.org/licenses/by/4.0/)
这本手册提供了多种语言的版本,查看 [自述文件](/README.md) 里的完整列表。
# 目录
* [介绍](#toc-introduction)
* [安装 Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [在项目内运行 Babel CLI](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [配置 Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [执行 Babel 生成的代码](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [配置 Babel(进阶)](#toc-configuring-babel-advanced)
* [手动指定插件](#toc-manually-specifying-plugins)
* [插件选项](#toc-plugin-options)
* [基于环境自定义 Babel](#toc-customizing-babel-based-on-environment)
* [制作你自己的预设(preset)](#toc-making-your-own-preset)
* [Babel 和其他工具](#toc-babel-and-other-tools)
* [静态分析工具](#toc-static-analysis-tools)
* [语法检查(Linting)](#toc-linting)
* [代码风格](#toc-code-style)
* [文档](#toc-documentation)
* [框架](#toc-frameworks)
* [React](#toc-react)
* [文本编辑器和 IDEs(集成开发环境)](#toc-text-editors-and-ides)
* [Babel 支持](#toc-babel-support)
* [Babel 论坛](#toc-babel-forum)
* [Babel 聊天](#toc-babel-chat)
* [Babel 问题](#toc-babel-issues)
* [创建漂亮的 Babel 错误报告](#toc-creating-an-awesome-babel-bug-report)
# 介绍
Babel 是一个通用的多用途 JavaScript 编译器。通过 Babel 你可以使用(并创建)下一代的 JavaScript,以及下一代的 JavaScript 工具。
作为一种语言,JavaScript 在不断发展,新的标准/提案和新的特性层出不穷。 在得到广泛普及之前,Babel 能够让你提前(甚至数年)使用它们。
Babel 把用最新标准编写的 JavaScript 代码向下编译成可以在今天随处可用的版本。 这一过程叫做“源码到源码”编译, 也被称为转换编译(transpiling,是一个自造合成词,即转换+编译。以下也简称为转译)。
例如,Babel 能够将新的 ES2015 箭头函数语法:
```js
const square = n => n * n;
```
转译为:
```js
const square = function square(n) {
return n * n;
};
```
不过 Babel 的用途并不止于此,它支持语法扩展,能支持像 React 所用的 JSX 语法,同时还支持用于静态类型检查的流式语法(Flow Syntax)。
更重要的是,Babel 的一切都是简单的插件,谁都可以创建自己的插件,利用 Babel 的全部威力去做任何事情。
*再进一步*,Babel 自身被分解成了数个核心模块,任何人都可以利用它们来创建下一代的 JavaScript 工具。
已经有很多人都这样做了,围绕着 Babel 涌现出了非常大规模和多样化的生态系统。 在这本手册中,我将介绍如何使用 Babel 的内建工具以及一些来自于社区的非常有用的东西。
> ***在 Twitter 上关注 [@thejameskyle](https://twitter.com/thejameskyle),第一时间获取更新。***
* * *
# 安装 Babel
由于 JavaScript 社区没有统一的构建工具、框架、平台等等,因此 Babel 正式集成了对所有主流工具的支持。 从 Gulp 到 Browserify,从 Ember 到 Meteor,不管你的环境设置如何,Babel 都有正式的集成支持。
本手册的目的主要是介绍 Babel 内建方式的安装,不过你可以访问交互式的[安装页面](http://babeljs.io/docs/setup)来查看其它的整合方式。
> **注意:** 本手册将涉及到一些命令行工具如 `node` 和 `npm`。在继续阅读之前请确保你已经熟悉这些工具了。
## `babel-cli`
Babel 的 CLI 是一种在命令行下使用 Babel 编译文件的简单方法。
让我们先全局安装它来学习基础知识。
```sh
$ npm install --global babel-cli
```
我们可以这样来编译我们的第一个文件:
```sh
$ babel my-file.js
```
这将把编译后的结果直接输出至终端。使用 `--out-file` 或着 `-o` 可以将结果写入到指定的文件。.
```sh
$ babel example.js --out-file compiled.js
# 或
$ babel example.js -o compiled.js
```
如果我们想要把一个目录整个编译成一个新的目录,可以使用 `--out-dir` 或者 `-d`。.
```sh
$ babel src --out-dir lib
# 或
$ babel src -d lib
```
### 在项目内运行 Babel CLI
尽管你*可以*把 Babel CLI 全局安装在你的机器上,但是按项目逐个安装在**本地**会更好。
有两个主要的原因。
1. 在同一台机器上的不同项目或许会依赖不同版本的 Babel 并允许你有选择的更新。
2. 这意味着你对工作环境没有隐式依赖,这让你的项目有很好的可移植性并且易于安装。
要在(项目)本地安装 Babel CLI 可以运行:
```sh
$ npm install --save-dev babel-cli
```
> **注意:**因为全局运行 Babel 通常不是什么好习惯所以如果你想要卸载全局安装的 Babel 的话,可以运行:
>
> ```sh
$ npm uninstall --global babel-cli
```
安装完成后,你的 `package.json` 应该如下所示:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
现在,我们不直接从命令行运行 Babel 了,取而代之我们将把运行命令写在 **npm scripts** 里,这样可以使用 Babel 的本地版本。
只需将 `"scripts"` 字段添加到你的 `package.json` 文件内并且把 babel 命令写成 `build` 字段。.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
现在可以在终端里运行:
```js
npm run build
```
这将以与之前同样的方式运行 Babel,但这一次我们使用的是本地副本。
## `babel-register`
下一个常用的运行 Babel 的方法是通过 `babel-register`。这种方法只需要引入文件就可以运行 Babel,或许能更好地融入你的项目设置。
但请注意这种方法并不适合正式产品环境使用。 直接部署用此方式编译的代码不是好的做法。 在部署之前预先编译会更好。 不过用在构建脚本或是其他本地运行的脚本中是非常合适的。
让我们先在项目中创建 `index.js` 文件。
```js
console.log("Hello world!");
```
如果我们用 `node index.js` 来运行它是不会使用 Babel 来编译的。所以我们需要设置 `babel-register`。.
首先安装 `babel-register`。.
```sh
$ npm install --save-dev babel-register
```
接着,在项目中创建 `register.js` 文件并添加如下代码:
```js
require("babel-register");
require("./index.js");
```
这样做可以把 Babel *注册*到 Node 的模块系统中并开始编译其中 `require` 的所有文件。
现在我们可以使用 `register.js` 来代替 `node index.js` 来运行了。
```sh
$ node register.js
```
> **注意:**你不能在你要编译的文件内同时注册 Babel,因为 node 会在 Babel 编译它之前就将它执行了。
>
> ```js
require("babel-register");
// 未编译的:
console.log("Hello world!");
```
## `babel-node`
如果你要用 `node` CLI 来运行代码,那么整合 Babel 最简单的方式就是使用 `babel-node` CLI,它是 `node` CLI 的替代品。
但请注意这种方法并不适合正式产品环境使用。 直接部署用此方式编译的代码不是好的做法。 在部署之前预先编译会更好。 不过用在构建脚本或是其他本地运行的脚本中是非常合适的。
首先确保 `babel-cli` 已经安装了。
```sh
$ npm install --save-dev babel-cli
```
> **注意:** 如果您想知道我们为什么要在本地安装,请阅读 上面[在项目内运行Babel CLI](#toc-running-babel-cli-from-within-a-project)的部分。
然后用 `babel-node` 来替代 `node` 运行所有的代码 。.
如果用 npm `scripts` 的话只需要这样做:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
要不然的话你需要写全 `babel-node` 的路径。
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> 提示:你可以使用 [`npm-run`](https://www.npmjs.com/package/npm-run)。.
## `babel-core`
如果你需要以编程的方式来使用 Babel,可以使用 `babel-core` 这个包。
首先安装 `babel-core`。.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
字符串形式的 JavaScript 代码可以直接使用 `babel.transform` 来编译。.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
如果是文件的话,可以使用异步 api:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
或者是同步 api:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
要是已经有一个 Babel AST(抽象语法树)了就可以直接从 AST 进行转换。
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
对于上述所有方法,`options` 指的都是 http://babeljs.io/docs/usage/options/
* * *
# 配置 Babel
你或许已经注意到了,目前为止通过运行 Babel 自己我们并没能“翻译”代码,而仅仅是把代码从一处拷贝到了另一处。
这是因为我们还没告诉 Babel 要做什么。
> 由于 Babel 是一个可以用各种花样去使用的通用编译器,因此默认情况下它反而什么都不做。你必须明确地告诉 Babel 应该要做什么。
你可以通过安装**插件(plugins)**或**预设(presets,也就是一组插件)**来指示 Babel 去做什么事情。
## `.babelrc`
在我们告诉 Babel 该做什么之前,我们需要创建一个配置文件。你需要做的就是在项目的根路径下创建 `.babelrc` 文件。然后输入以下内容作为开始:
```js
{
"presets": [],
"plugins": []
}
```
这个文件就是用来让 Babel 做你要它做的事情的配置文件。
> **注意:**尽管你也可以用其他方式给 Babel 传递选项,但 `.babelrc` 文件是约定也是最好的方式。
## `babel-preset-es2015`
我们先从让 Babel 把 ES2015(最新版本的 JavaScript 标准,也叫做 ES6)编译成 ES5(现今在大多数 JavaScript 环境下可用的版本)开始吧。
我们需要安装 "es2015" Babel 预设:
```sh
$ npm install --save-dev babel-preset-es2015
```
我们修改 `.babelrc` 来包含这个预设。
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
设置 React 一样容易。只需要安装这个预设:
```sh
$ npm install --save-dev babel-preset-react
```
然后在 `.babelrc` 文件里补充:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript 还有一些提案,正在积极通过 TC39(ECMAScript 标准背后的技术委员会)的流程成为标准的一部分。
这个流程分为 5(0-4)个阶段。 随着提案得到越多的关注就越有可能被标准采纳,于是他们就继续通过各个阶段,最终在阶段 4 被标准正式采纳。
以下是4 个不同阶段的(打包的)预设:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> 注意 stage-4 预设是不存在的因为它就是上面的 `es2015` 预设。
以上每种预设都依赖于紧随的后期阶段预设。例如,`babel-preset-stage-1` 依赖 `babel-preset-stage-2`,后者又依赖 `babel-preset-stage-3`。.
使用的时候只需要安装你想要的阶段就可以了:
```sh
$ npm install --save-dev babel-preset-stage-2
```
然后添加进你的 `.babelrc` 配置文件。
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# 执行 Babel 生成的代码
即便你已经用 Babel 编译了你的代码,但这还不算完。
## `babel-polyfill`
Babel 几乎可以编译所有时新的 JavaScript 语法,但对于 APIs 来说却并非如此。
比方说,下列含有箭头函数的需要编译的代码:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
最终会变成这样:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
然而,它依然无法随处可用因为不是所有的 JavaScript 环境都支持 `Array.from`。
Uncaught TypeError: Array.from is not a function
为了解决这个问题,我们使用一种叫做 [Polyfill(代码填充,也可译作兼容性补丁)](https://remysharp.com/2010/10/08/what-is-a-polyfill) 的技术。 简单地说,polyfill 即是在当前运行环境中用来复制(意指模拟性的复制,而不是拷贝)尚不存在的原生 api 的代码。 能让你提前使用还不可用的 APIs,`Array.from` 就是一个例子。
Babel 用了优秀的 [core-js](https://github.com/zloirock/core-js) 用作 polyfill,并且还有定制化的 [regenerator](https://github.com/facebook/regenerator) 来让 generators(生成器)和 async functions(异步函数)正常工作。
要使用 Babel polyfill,首先用 npm 安装它:
```sh
$ npm install --save babel-polyfill
```
然后只需要在文件顶部导入 polyfill 就可以了:
```js
import "babel-polyfill";
```
## `babel-runtime`
为了实现 ECMAScript 规范的细节,Babel 会使用“助手”方法来保持生成代码的整洁。
由于这些助手方法可能会特别长并且会被添加到每一个文件的顶部,因此你可以把它们统一移动到一个单一的“运行时(runtime)”中去。
通过安装 `babel-plugin-transform-runtime` 和 `babel-runtime` 来开始。
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
然后更新 `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
现在,Babel 会把这样的代码:
```js
class Foo {
method() {}
}
```
编译成:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
这样就不需要把 `_classCallCheck` 和 `_createClass` 这两个助手方法放进每一个需要的文件里去了。
* * *
# 配置 Babel(进阶)
大多数人使用 Babel 的内建预设就足够了,不过 Babel 提供了更多更细粒度的能力。
## 手动指定插件
Babel 预设就是一些预先配置好的插件的集合,如果你想要做一些不一样的事情你会手动去设定插件,这和使用预设几乎完全相同。
首先安装插件:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
然后往 `.babelrc` 文件添加 `plugins` 字段。.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
这能让你对正在使用的转换器进行更细致的控制。
完整的官方插件列表请见 [Babel 插件页面](http://babeljs.io/docs/plugins/)。.
同时也别忘了看看[由社区构建的其他插件](https://www.npmjs.com/search?q=babel-plugin)。 如果你想学习如何编写自己的插件可以阅读 [Babel 插件手册](plugin-handbook.md)。.
## 插件选项
很多插件也有选项用于配置他们自身的行为。 例如,很多转换器都有“宽松”模式,通过放弃一些标准中的行为来生成更简化且性能更好的代码。
要为插件添加选项,只需要做出以下更改:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> 接下来几周内我会更新插件文档来详细介绍每一个选项。[关注我以获知更新](https://twitter.com/thejameskyle)。.
## 基于环境自定义 Babel
Babel 插件解决许多不同的问题。 其中大多数是开发工具,可以帮助你调试代码或是与工具集成。 也有大量的插件用于在生产环境中优化你的代码。
因此,想要基于环境来配置 Babel 是很常见的。你可以轻松的使用 `.babelrc` 文件来达成目的。
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel 将根据当前环境来开启 `env` 下的配置。
当前环境可以使用 `process.env.BABEL_ENV` 来获得。 如果 `BABEL_ENV` 不可用,将会替换成 `NODE_ENV`,并且如果后者也没有设置,那么缺省值是`"development"`。.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **注意:**`[COMMAND]` 指的是任意一个用来运行 Babel 的命令(如:`babel`,`babel-node`,或是 `node`,如果你使用了 register 钩子的话)。
>
> **提示:**如果你想要让命令能够跨 unix 和 windows 平台运行的话,可以使用 [`cross-env`](https://www.npmjs.com/package/cross-env)。.
## 制作你自己的预设(preset)
手动指定插件?插件选项?环境特定设置?所有这些配置都会在你的项目里产生大量的重复工作。
为此,我们鼓励社区创建自己的预设。 这可能是一个针对特定 [node 版本](https://github.com/leebenson/babel-preset-node5)的预设,或是适用于你[整个](https://github.com/cloudflare/babel-preset-cf)[公司](https://github.com/airbnb/babel-preset-airbnb)的预设。.
创建预设非常容易。比方说你这样一个 `.babelrc` 文件:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
你要做的就是依循命名约定 `babel-preset-*` 来创建一个新项目(请务必对这个命名约定保持责任心,也就是说不要滥用这个命名空间),然后创建两个文件。
首先,创建一个 `package.json`,包括针对预设所必要的 `dependencies`。
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
然后创建 `index.js` 文件用于导出 `.babelrc` 的内容,使用对应的 `require` 调用来替换 plugins/presets 字符串。
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
然后只需要发布到 npm 于是你就可以像其它预设一样来使用你的预设了。
* * *
# Babel 和其他工具
一旦你掌握的窍门,安装 Babel 还是十分简明的,不过和其他工具搭配在一起就会变得困难多了。 不过我们一直在与其他项目密切合作以确保这种体验尽可能简单。
## 静态分析工具
新标准为语言带来了许多新的语法,静态分析工具正在将此利用起来。
### 语法检查(Linting)
[ESLint](http://eslint.org) 是最流行的语法检查工具之一,因此我们维护了一个官方的 [`babel-eslint`](https://github.com/babel/babel-eslint) 整合软件包。
首先安装 `eslint` 和 `babel-eslint`。.
```sh
$ npm install --save-dev eslint babel-eslint
```
然后创建或使用项目现有的 `.eslintrc` 文件并设置 `parser` 为 `babel-eslint`。.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
现在添加一个 `lint` 任务到 npm 的 `package.json` 脚本中:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
接着只需要运行这个任务就一切就绪了。
```sh
$ npm run lint
```
详细信息请咨询 [`babel-eslint`](https://github.com/babel/babel-eslint) 或者 [`eslint`](http://eslint.org) 的文档。
### 代码风格
> JSCS已经和ESLint合并,所以请查看ESLint的代码风格。
JSCS 是一个极受欢迎的工具,在语法检查的基础上更进一步检查代码自身的风格。 Babel 和 JSCS 项目的核心维护者之一([@hzoo](https://github.com/hzoo))维护着 JSCS 的官方集成。
更妙的是,JSCS 自己通过 `--esnext` 选项实现了这种集成,于是和 Babel 的集成就简化成了直接在命令行运行:
$ jscs . --esnext
或者在 `.jscsrc` 文件里添加 `esnext` 选项。
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
详细信息请咨询 [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) 或是 [`jscs`](http://jscs.info) 的文档。
### 文档
使用 Babel,ES2015,还有 Flow 你可以对你的代码进行大量的推断。使用 [documentation.js](http://documentation.js.org) 可以非常简便地生成详细的 API 文档。
Documentation.js 使用 Babel 来支持所有最新的语法,包括用于在你的代码中声明类型所用的 Flow 注解在内,
## 框架
所有主流的 JavaScript 框架都正在努力调整他们的 APIs 向这门语言的未来看齐。有鉴于此,配套工具方面已经做出了大量的工作。
除了使用 Babel 以外,框架更有条件去扩展 Babel 来帮助他们提升用户体验。
### React
React 已经大幅改变了他们的 API 以适应 ES2015 的类语法([此处了解更新的 API](https://babeljs.io/blog/2015/06/07/react-on-es6-plus))。 特别是 React 现在依赖 Babel 编译它的 JSX 语法且弃用了它原有的自定义工具。 你可以按照[上述说明](#babel-preset-react)安装 `babel-preset-react` 包来开始。.
React 社区采用 Babel 并围绕它来运行,现在社区已经创建了[大量的转换器(transforms)](https://www.npmjs.com/search?q=babel-plugin+react)。.
最令人瞩目的是 [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) 插件,它集成了大量 [React 专用转换器](https://github.com/gaearon/babel-plugin-react-transform#transforms)可以启用诸如 *热模块重载*等其他调试工具。
## 文本编辑器和 IDEs(集成开发环境)
通过 Babel 引入 ES2015,JSX,和流式语法固然是大有裨益,可如果你的文本编辑不支持那可就糟糕透了。 因此,别忘了为你的文本编辑器或是 IDE 安装 Babel 插件。
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# Babel 支持
Babel 的社区非常庞大并且增长速度很快,伴随着我们成长的同时我们希望保证人们总能获取他们需要的所有资源。 所以我们提供了数种途径来提供支持。
谨记在所有的这些沟通渠道里我们都共同遵守一套[行为准则](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md)。 破坏准则的行为会被处理。 所以请阅读它并在与他人互动时注意自己的行为。
同时我们也在寻求发展一个自我支持式的社区,为那些始终热诚奉献的人们。 如果别人问的问题你恰好知道答案,请不吝花费几分钟帮帮他们。 在此过程中也请尽力保持友善与相互理解。
## Babel 论坛
[Discourse](http://www.discourse.org) 免费为我们提供了一个托管版本的论坛(我们爱死他们了!)。 如果你是个论坛控请不要错过 [discuss.babeljs.io](https://discuss.babeljs.io)。.
## Babel 聊天
无人不爱 [Slack](https://slack.com)。如果你正在寻求来自社区的即时支持,那就来 [slack.babeljs.io](https://slack.babeljs.io) 和我们聊天吧。.
## Babel 问题
Babel使用[Github](http://github.com)提供的问题跟踪器。.
您可以在[Github](https://github.com/babel/babel/issues)上看到所有的开放和封闭的问题.
如果你想要打开一个新的问题:
* [先搜搜看有没有现存的类似问题](https://github.com/babel/babel/issues)
* 创建一个新的错误报告> 或请求新功能>
### 创建漂亮的 Babel 错误报告
Babel 的问题有时候很难远程调试,所以我们希望能获取尽可能详细的信息来帮助我们解决问题。 花点时间去撰写一份好的错误报告会让你的问题更快得到解决。
首先,尝试隔离问题。 并非设置过程的每一步都是导致问题的原因。 如果你的问题是一段输入代码,试着尽可能把与问题不相关的代码都删除掉。
> [WIP]
* * *
> ***在 Twitter 上关注 [@thejameskyle](https://twitter.com/thejameskyle),第一时间获取更新。***
================================================
FILE: translations/zh-Hant/README.md
================================================
# Babel 使用手冊
本手冊分為兩部分:
* [使用者手冊](user-handbook.md) - 如何安裝與配置 Babel
* [外掛手冊](plugin-handbook.md) - 如何撰寫 Babel 的外掛程式
> 進一步的最新資訊,請追蹤[@thejameskyle](https://twitter.com/thejameskyle)的 Twitter 帳號。
如果你正在閱讀這份非英語版的手冊,你會發現其中仍有尚未翻譯完成的部分。 如果你想協助翻譯這份文件,你得透過 Crowdin 來參與。 詳情請參閱 [協助準則](/CONTRIBUTING.md)。 在過程中,你將會遇到許多程式設計上的專用術語, 如果按字面上的意義來直譯,會讓人覺得閱讀起來缺乏連貫性與流暢性。 在大多數情況下,你可在該術語的翻譯之後,加上`()`並附上英文原文。 例如: 抽象語法樹 (Abstract Syntax Tree, ASTs).
================================================
FILE: translations/zh-Hant/plugin-handbook.md
================================================
# Babel 外掛手冊
這份文件提共如何使用 [Babel](https://babeljs.io)[外掛](https://babeljs.io/docs/advanced/plugins/).
[](http://creativecommons.org/licenses/by/4.0/)
手冊提共其它可用語系,可在[閱讀更多](/README.md)找到完整清單
# 目錄
* [簡介](#toc-introduction)
* [基本功能](#toc-basics)
* [ASTs (抽象語法樹)](#toc-asts)
* [Babel 的階段](#toc-stages-of-babel)
* [解析 (Parse)](#toc-parse)
* [Lexical Analysis](#toc-lexical-analysis)
* [Syntactic Analysis](#toc-syntactic-analysis)
* [轉換 (Transform)](#toc-transform)
* [生成 (Generate)](#toc-generate)
* [遍歷 (Traversal)](#toc-traversal)
* [訪問者 (Visitors)](#toc-visitors)
* [路徑 (Paths)](#toc-paths)
* [Paths in Visitors](#toc-paths-in-visitors)
* [狀態 (State)](#toc-state)
* [範圍 (Scopes)](#toc-scopes)
* [Bindings](#toc-bindings)
* [API](#toc-api)
* [babylon](#toc-babylon)
* [babel-traverse](#toc-babel-traverse)
* [babel-types](#toc-babel-types)
* [定義](#toc-definitions)
* [建置](#toc-builders)
* [驗證](#toc-validators)
* [轉換器](#toc-converters)
* [babel-generator](#toc-babel-generator)
* [babel-template](#toc-babel-template)
* [撰寫你的第一個 Babel 外掛](#toc-writing-your-first-babel-plugin)
* [Transformation Operations](#toc-transformation-operations)
* [Visiting](#toc-visiting)
* [Get the Path of Sub-Node](#toc-get-the-path-of-a-sub-node)
* [Check if a node is a certain type](#toc-check-if-a-node-is-a-certain-type)
* [Check if a path is a certain type](#toc-check-if-a-path-is-a-certain-type)
* [Check if an identifier is referenced](#toc-check-if-an-identifier-is-referenced)
* [Find a specific parent path](#toc-find-a-specific-parent-path)
* [Get Sibling Paths](#toc-get-sibling-paths)
* [Stopping Traversal](#toc-stopping-traversal)
* [Manipulation](#toc-manipulation)
* [Replacing a node](#toc-replacing-a-node)
* [Replacing a node with multiple nodes](#toc-replacing-a-node-with-multiple-nodes)
* [Replacing a node with a source string](#toc-replacing-a-node-with-a-source-string)
* [Inserting a sibling node](#toc-inserting-a-sibling-node)
* [Inserting into a container](#toc-inserting-into-a-container)
* [Removing a node](#toc-removing-a-node)
* [Replacing a parent](#toc-replacing-a-parent)
* [Removing a parent](#toc-removing-a-parent)
* [Scope](#toc-scope)
* [Checking if a local variable is bound](#toc-checking-if-a-local-variable-is-bound)
* [Generating a UID](#toc-generating-a-uid)
* [Pushing a variable declaration to a parent scope](#toc-pushing-a-variable-declaration-to-a-parent-scope)
* [Rename a binding and its references](#toc-rename-a-binding-and-its-references)
* [Plugin Options](#toc-plugin-options)
* [Pre and Post in Plugins](#toc-pre-and-post-in-plugins)
* [Enabling Syntax in Plugins](#toc-enabling-syntax-in-plugins)
* [Building Nodes](#toc-building-nodes)
* [Best Practices](#toc-best-practices)
* [Avoid traversing the AST as much as possible](#toc-avoid-traversing-the-ast-as-much-as-possible)
* [Merge visitors whenever possible](#toc-merge-visitors-whenever-possible)
* [Do not traverse when manual lookup will do](#toc-do-not-traverse-when-manual-lookup-will-do)
* [Optimizing nested visitors](#toc-optimizing-nested-visitors)
* [Being aware of nested structures](#toc-being-aware-of-nested-structures)
* [Unit Testing](#toc-unit-testing)
# 簡介
Babel 是一個廣泛、多目的的 Javascript 編譯器。不止這樣,它還是多個模組的集合,可以讓我們做各種不同的靜態解析。
> 靜態解析是指我們可以直接分析程式碼而不用去執行它們。 (若是需要執行才能解析,就是一般我們所熟知的動態解析。) 靜態解析的目的非常多樣, 可以用於:linting, compiling, code highlighting, code transformation, optimization, minification 或其他更多的地方
使用 Babel 可以建構各種不同的工具,幫助你更有效的製作產品或是寫出好的程式。
> ***更多的最新資訊,請上 [@thejameskyle](https://twitter.com/thejameskyle) 的 Twitter 查詢。***
* * *
# 基本功能
Babel 是一個 JavaScript compiler,因為是 source-to-source 而非 source-to-binary 的 compiler,所以通常又稱為 "transpiler"。 這表示當你餵給 Babel 一些 JavaScript code,Babel 會將之修改之後,回給你一份修改後的新 code。
## ASTs (抽象語法樹)
每一個步驟包含 [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) 或 AST 的建造或是使用
> Babel uses an AST modified from [ESTree](https://github.com/estree/estree), with the core spec located [here](https://github.com/babel/babylon/blob/master/ast/spec.md).
```js
function square(n) {
return n * n;
}
```
> 在 [AST Explorer](http://astexplorer.net/)中可以對 AST nodes 有更深入的了解, [點我](http://astexplorer.net/#/Z1exs6BWMq)可以連到上述範例代碼的頁面。
This same program can be represented as a tree like this:
```md
- FunctionDeclaration:
- id:
- Identifier:
- name: square
- params [1]
- Identifier
- name: n
- body:
- BlockStatement
- body [1]
- ReturnStatement
- argument
- BinaryExpression
- operator: *
- left
- Identifier
- name: n
- right
- Identifier
- name: n
```
或是像這樣的JS的物件:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
你會發現到每一個 AST 的層級有相似的結構:
```js
{
type: "FunctionDeclaration",
id: {...},
params: [...],
body: {...}
}
```
```js
{
type: "Identifier",
name: ...
}
```
```js
{
type: "BinaryExpression",
operator: ...,
left: {...},
right: {...}
}
```
> 備註:為了簡單起見,某些屬性已經被移除。
一個AST可以為單個,數百個甚至於千個節點, 而這些都也都被稱為節點 <0>Node0>。 Together they are able to describe the syntax of a program that can be used for static analysis.
每個節點都有這一個的物件:
```typescript
interface Node {
type: string;
}
```
`類型`是一個文字的型態,代表節點物件的類別(意即 `"FunctionDeclaration"`, `"Identifier"`, or `"BinaryExpression"`). Each type of Node defines an additional set of properties that describe that particular node type.
There are additional properties on every Node that Babel generates which describe the position of the Node in the original source code.
```js
{
type: ...,
start: 0,
end: 38,
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 3,
column: 1
}
},
...
}
```
These properties `start`, `end`, `loc`, appear in every single Node.
## Babel 的階段
The three primary stages of Babel are **parse**, **transform**, **generate**.
### 解析 (Parse)
The **parse** stage, takes code and outputs an AST. There are two phases of parsing in Babel: [**Lexical Analysis**](https://en.wikipedia.org/wiki/Lexical_analysis) and [**Syntactic Analysis**](https://en.wikipedia.org/wiki/Parsing).
#### Lexical Analysis
Lexical Analysis will take a string of code and turn it into a stream of **tokens**.
You can think of tokens as a flat array of language syntax pieces.
```js
n * n;
```
```js
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
```
在這裡的每個 `類別`都有一組描述其屬性的文字:
```js
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
```
Like AST nodes they also have a `start`, `end`, and `loc`.
#### 語意分析
Syntactic Analysis will take a stream of tokens and turn it into an AST representation. Using the information in the tokens, this phase will reformat them as an AST which represents the structure of the code in a way that makes it easier to work with.
### 轉換 (Transform)
The [transform](https://en.wikipedia.org/wiki/Program_transformation) stage takes an AST and traverses through it, adding, updating, and removing nodes as it goes along. This is by far the most complex part of Babel or any compiler. This is where plugins operate and so it will be the subject of most of this handbook. So we won't dive too deep right now.
### 生成 (Generate)
The [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) stage takes the final AST and turns it back into a string of code, also creating [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/).
Code generation is pretty simple: you traverse through the AST depth-first, building a string that represents the transformed code.
## 遍歷 (Traversal)
When you want to transform an AST you have to [traverse the tree](https://en.wikipedia.org/wiki/Tree_traversal) recursively.
Say we have the type `FunctionDeclaration`. It has a few properties: `id`, `params`, and `body`. Each of them have nested nodes.
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
params: [{
type: "Identifier",
name: "n"
}],
body: {
type: "BlockStatement",
body: [{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "n"
},
right: {
type: "Identifier",
name: "n"
}
}
}]
}
}
```
So we start at the `FunctionDeclaration` and we know its internal properties so we visit each of them and their children in order.
Next we go to `id` which is an `Identifier`. `Identifier`s don't have any child node properties so we move on.
After that is `params` which is an array of nodes so we visit each of them. In this case it's a single node which is also an `Identifier` so we move on.
Then we hit `body` which is a `BlockStatement` with a property `body` that is an array of Nodes so we go to each of them.
The only item here is a `ReturnStatement` node which has an `argument`, we go to the `argument` and find a `BinaryExpression`.
The `BinaryExpression` has an `operator`, a `left`, and a `right`. The operator isn't a node, just a value, so we don't go to it, and instead just visit `left` and `right`.
This traversal process happens throughout the Babel transform stage.
### 訪問者 (Visitors)
When we talk about "going" to a node, we actually mean we are **visiting** them. The reason we use that term is because there is this concept of a [**visitor**](https://en.wikipedia.org/wiki/Visitor_pattern).
Visitors are a pattern used in AST traversal across languages. Simply put they are an object with methods defined for accepting particular node types in a tree. That's a bit abstract so let's look at an example.
```js
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// You can also create a visitor and add methods on it later
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
```
> **Note:** `Identifier() { ... }` is shorthand for `Identifier: { enter() { ... } }`.
This is a basic visitor that when used during a traversal will call the `Identifier()` method for every `Identifier` in the tree.
So with this code the `Identifier()` method will be called four times with each `Identifier` (including `square`).
```js
function square(n) {
return n * n;
}
```
```js
path.traverse(MyVisitor);
Called!
Called!
Called!
Called!
```
These calls are all on node **enter**. However there is also the possibility of calling a visitor method when on **exit**.
Imagine we have this tree structure:
```js
- FunctionDeclaration
- Identifier (id)
- Identifier (params[0])
- BlockStatement (body)
- ReturnStatement (body)
- BinaryExpression (argument)
- Identifier (left)
- Identifier (right)
```
As we traverse down each branch of the tree we eventually hit dead ends where we need to traverse back up the tree to get to the next node. Going down the tree we **enter** each node, then going back up we **exit** each node.
Let's *walk* through what this process looks like for the above tree.
* Enter `FunctionDeclaration`
* Enter `Identifier (id)`
* Hit dead end
* Exit `Identifier (id)`
* Enter `Identifier (params[0])`
* Hit dead end
* Exit `Identifier (params[0])`
* Enter `BlockStatement (body)`
* Enter `ReturnStatement (body)`
* Enter `BinaryExpression (argument)`
* Enter `Identifier (left)`
* Hit dead end
* Exit `Identifier (left)`
* Enter `Identifier (right)`
* Hit dead end
* Exit `Identifier (right)`
* Exit `BinaryExpression (argument)`
* Exit `ReturnStatement (body)`
* Exit `BlockStatement (body)`
* Exit `FunctionDeclaration`
So when creating a visitor you have two opportunities to visit a node.
```js
const MyVisitor = {
Identifier: {
enter() {
console.log("Entered!");
},
exit() {
console.log("Exited!");
}
}
};
```
If necessary, you can also apply the same function for multiple visitor nodes by separating them with a `|` in the method name as a string like `Identifier|MemberExpression`.
Example usage in the [flow-comments](https://github.com/babel/babel/blob/2b6ff53459d97218b0cf16f8a51c14a165db1fd2/packages/babel-plugin-transform-flow-comments/src/index.js#L47) plugin
```js
const MyVisitor = {
"ExportNamedDeclaration|Flow"(path) {}
};
```
You can also use aliases as visitor nodes (as defined in [babel-types](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions)).
For example,
`Function` is an alias for `FunctionDeclaration`, `FunctionExpression`, `ArrowFunctionExpression`, `ObjectMethod` and `ClassMethod`.
```js
const MyVisitor = {
Function(path) {}
};
```
### 路徑 (Paths)
An AST generally has many Nodes, but how do Nodes relate to one another? We could have one giant mutable object that you manipulate and have full access to, or we can simplify this with **Paths**.
A **Path** is an object representation of the link between two nodes.
For example if we take the following node and its child:
```js
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "square"
},
...
}
```
And represent the child `Identifier` as a path, it looks something like this:
```js
{
"parent": {
"type": "FunctionDeclaration",
"id": {...},
....
},
"node": {
"type": "Identifier",
"name": "square"
}
}
```
It also has additional metadata about the path:
```js
{
"parent": {...},
"node": {...},
"hub": {...},
"contexts": [],
"data": {},
"shouldSkip": false,
"shouldStop": false,
"removed": false,
"state": null,
"opts": null,
"skipKeys": null,
"parentPath": null,
"context": null,
"container": null,
"listKey": null,
"inList": false,
"parentKey": null,
"key": null,
"scope": null,
"type": null,
"typeAnnotation": null
}
```
As well as tons and tons of methods related to adding, updating, moving, and removing nodes, but we'll get into those later.
In a sense, paths are a **reactive** representation of a node's position in the tree and all sorts of information about the node. Whenever you call a method that modifies the tree, this information is updated. Babel manages all of this for you to make working with nodes easy and as stateless as possible.
#### Paths in Visitors
When you have a visitor that has a `Identifier()` method, you're actually visiting the path instead of the node. This way you are mostly working with the reactive representation of a node instead of the node itself.
```js
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
```
```js
a + b + c;
```
```js
path.traverse(MyVisitor);
Visiting: a
Visiting: b
Visiting: c
```
### 狀態 (State)
State is the enemy of AST transformation. State will bite you over and over again and your assumptions about state will almost always be proven wrong by some syntax that you didn't consider.
Take the following code:
```js
function square(n) {
return n * n;
}
```
Let's write a quick hacky visitor that will rename `n` to `x`.
```js
let paramName;
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
paramName = param.name;
param.name = "x";
},
Identifier(path) {
if (path.node.name === paramName) {
path.node.name = "x";
}
}
};
```
This might work for the above code, but we can easily break that by doing this:
```js
function square(n) {
return n * n;
}
n;
```
The better way to deal with this is recursion. So let's make like a Christopher Nolan film and put a visitor inside of a visitor.
```js
const updateParamNameVisitor = {
Identifier(path) {
if (path.node.name === this.paramName) {
path.node.name = "x";
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
const param = path.node.params[0];
const paramName = param.name;
param.name = "x";
path.traverse(updateParamNameVisitor, { paramName });
}
};
path.traverse(MyVisitor);
```
Of course, this is a contrived example but it demonstrates how to eliminate global state from your visitors.
### 範圍 (Scopes)
Next let's introduce the concept of a [**scope**](https://en.wikipedia.org/wiki/Scope_(computer_science)). JavaScript has [lexical scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping_vs._dynamic_scoping), which is a tree structure where blocks create new scope.
```js
// global scope
function scopeOne() {
// scope 1
function scopeTwo() {
// scope 2
}
}
```
Whenever you create a reference in JavaScript, whether that be by a variable, function, class, param, import, label, etc., it belongs to the current scope.
```js
var global = "I am in the global scope";
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var two = "I am in the scope created by `scopeTwo()`";
}
}
```
Code within a deeper scope may use a reference from a higher scope.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
one = "I am updating the reference in `scopeOne` inside `scopeTwo`";
}
}
```
A lower scope might also create a reference of the same name without modifying it.
```js
function scopeOne() {
var one = "I am in the scope created by `scopeOne()`";
function scopeTwo() {
var one = "I am creating a new `one` but leaving reference in `scopeOne()` alone.";
}
}
```
When writing a transform, we want to be wary of scope. We need to make sure we don't break existing code while modifying different parts of it.
We may want to add new references and make sure they don't collide with existing ones. Or maybe we just want to find where a variable is referenced. We want to be able to track these references within a given scope.
A scope can be represented as:
```js
{
path: path,
block: path.node,
parentBlock: path.parent,
parent: parentScope,
bindings: [...]
}
```
When you create a new scope you do so by giving it a path and a parent scope. Then during the traversal process it collects all the references ("bindings") within that scope.
Once that's done, there's all sorts of methods you can use on scopes. We'll get into those later though.
#### Bindings
References all belong to a particular scope; this relationship is known as a **binding**.
```js
function scopeOnce() {
var ref = "This is a binding";
ref; // This is a reference to a binding
function scopeTwo() {
ref; // This is a reference to a binding from a lower scope
}
}
```
A single binding looks like this:
```js
{
identifier: node,
scope: scope,
path: path,
kind: 'var',
referenced: true,
references: 3,
referencePaths: [path, path, path],
constant: false,
constantViolations: [path]
}
```
With this information you can find all the references to a binding, see what type of binding it is (parameter, declaration, etc.), lookup what scope it belongs to, or get a copy of its identifier. You can even tell if it's constant and if not, see what paths are causing it to be non-constant.
Being able to tell if a binding is constant is useful for many purposes, the largest of which is minification.
```js
function scopeOne() {
var ref1 = "This is a constant binding";
becauseNothingEverChangesTheValueOf(ref1);
function scopeTwo() {
var ref2 = "This is *not* a constant binding";
ref2 = "Because this changes the value";
}
}
```
* * *
# API
Babel is actually a collection of modules. In this section we'll walk through the major ones, explaining what they do and how to use them.
> Note: This is not a replacement for detailed API documentation which will be available elsewhere shortly.
## [`babylon`](https://github.com/babel/babylon)
Babylon is Babel's parser. Started as a fork of Acorn, it's fast, simple to use, has plugin-based architecture for non-standard features (as well as future standards).
First, let's install it.
```sh
$ npm install --save babylon
```
Let's start by simply parsing a string of code:
```js
import * as babylon from "babylon";
const code = `function square(n) {
return n * n;
}`;
babylon.parse(code);
// Node {
// type: "File",
// start: 0,
// end: 38,
// loc: SourceLocation {...},
// program: Node {...},
// comments: [],
// tokens: [...]
// }
```
We can also pass options to `parse()` like so:
```js
babylon.parse(code, {
sourceType: "module", // default: "script"
plugins: ["jsx"] // default: []
});
```
`sourceType` can either be `"module"` or `"script"` which is the mode that Babylon should parse in. `"module"` will parse in strict mode and allow module declarations, `"script"` will not.
> **Note:** `sourceType` defaults to `"script"` and will error when it finds `import` or `export`. Pass `sourceType: "module"` to get rid of these errors.
Since Babylon is built with a plugin-based architecture, there is also a `plugins` option which will enable the internal plugins. Note that Babylon has not yet opened this API to external plugins, although may do so in the future.
To see a full list of plugins, see the [Babylon README](https://github.com/babel/babylon/blob/master/README.md#plugins).
## [`babel-traverse`](https://github.com/babel/babel/tree/master/packages/babel-traverse)
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes.
Install it by running:
```sh
$ npm install --save babel-traverse
```
We can use it alongside Babylon to traverse and update nodes:
```js
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (
path.node.type === "Identifier" &&
path.node.name === "n"
) {
path.node.name = "x";
}
}
});
```
## [`babel-types`](https://github.com/babel/babel/tree/master/packages/babel-types)
Babel Types is a Lodash-esque utility library for AST nodes. It contains methods for building, validating, and converting AST nodes. It's useful for cleaning up AST logic with well thought out utility methods.
You can install it by running:
```sh
$ npm install --save babel-types
```
Then start using it:
```js
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
```
### 定義
Babel Types has definitions for every single type of node, with information on what properties belong where, what values are valid, how to build that node, how the node should be traversed, and aliases of the Node.
A single node type definition looks like this:
```js
defineType("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
```
### 建置
You'll notice the above definition for `BinaryExpression` has a field for a `builder`.
```js
builder: ["operator", "left", "right"]
```
This is because each node type gets a builder method, which when used looks like this:
```js
t.binaryExpression("*", t.identifier("a"), t.identifier("b"));
```
Which creates an AST like this:
```js
{
type: "BinaryExpression",
operator: "*",
left: {
type: "Identifier",
name: "a"
},
right: {
type: "Identifier",
name: "b"
}
}
```
Which when printed looks like this:
```js
a * b
```
Builders will also validate the nodes they are creating and throw descriptive errors if used improperly. Which leads into the next type of method.
### 驗證
The definition for `BinaryExpression` also includes information on the `fields` of a node and how to validate them.
```js
fields: {
operator: {
validate: assertValueType("string")
},
left: {
validate: assertNodeType("Expression")
},
right: {
validate: assertNodeType("Expression")
}
}
```
This is used to create two types of validating methods. The first of which is `isX`.
```js
t.isBinaryExpression(maybeBinaryExpressionNode);
```
This tests to make sure that the node is a binary expression, but you can also pass a second parameter to ensure that the node contains certain properties and values.
```js
t.isBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
```
There is also the more, *ehem*, assertive version of these methods, which will throw errors instead of returning `true` or `false`.
```js
t.assertBinaryExpression(maybeBinaryExpressionNode);
t.assertBinaryExpression(maybeBinaryExpressionNode, { operator: "*" });
// Error: Expected type "BinaryExpression" with option { "operator": "*" }
```
### 轉換器
> [WIP]
## [`babel-generator`](https://github.com/babel/babel/tree/master/packages/babel-generator)
Babel Generator is the code generator for Babel. It takes an AST and turns it into code with sourcemaps.
Run the following to install it:
```sh
$ npm install --save babel-generator
```
Then use it
```js
import * as babylon from "babylon";
import generate from "babel-generator";
const code = `function square(n) {
return n * n;
}`;
const ast = babylon.parse(code);
generate(ast, {}, code);
// {
// code: "...",
// map: "..."
// }
```
You can also pass options to `generate()`.
```js
generate(ast, {
retainLines: false,
compact: "auto",
concise: false,
quotes: "double",
// ...
}, code);
```
## [`babel-template`](https://github.com/babel/babel/tree/master/packages/babel-template)
Babel Template is another tiny but incredibly useful module. It allows you to write strings of code with placeholders that you can use instead of manually building up a massive AST. In computer science, this capability is called quasiquotes.
```sh
$ npm install --save babel-template
```
```js
import template from "babel-template";
import generate from "babel-generator";
import * as t from "babel-types";
const buildRequire = template(`
var IMPORT_NAME = require(SOURCE);
`);
const ast = buildRequire({
IMPORT_NAME: t.identifier("myModule"),
SOURCE: t.stringLiteral("my-module")
});
console.log(generate(ast).code);
```
```js
var myModule = require("my-module");
```
# 撰寫你的第一個 Babel 外掛
Now that you're familiar with all the basics of Babel, let's tie it together with the plugin API.
Start off with a `function` that gets passed the current [`babel`](https://github.com/babel/babel/tree/master/packages/babel-core) object.
```js
export default function(babel) {
// plugin contents
}
```
Since you'll be using it so often, you'll likely want to grab just `babel.types` like so:
```js
export default function({ types: t }) {
// plugin contents
}
```
Then you return an object with a property `visitor` which is the primary visitor for the plugin.
```js
export default function({ types: t }) {
return {
visitor: {
// visitor contents
}
};
};
```
Each function in the visitor receives 2 arguments: `path` and `state`
```js
export default function({ types: t }) {
return {
visitor: {
Identifier(path, state) {},
ASTNodeTypeHere(path, state) {}
}
};
};
```
Let's write a quick plugin to show off how it works. Here's our source code:
```js
foo === bar;
```
Or in AST form:
```js
{
type: "BinaryExpression",
operator: "===",
left: {
type: "Identifier",
name: "foo"
},
right: {
type: "Identifier",
name: "bar"
}
}
```
We'll start off by adding a `BinaryExpression` visitor method.
```js
export default function({ types: t }) {
return {
visitor: {
BinaryExpression(path) {
// ...
}
}
};
}
```
Then let's narrow it down to just `BinaryExpression`s that are using the `===` operator.
```js
visitor: {
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
// ...
}
}
```
Now let's replace the `left` property with a new identifier:
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
// ...
}
```
Already if we run this plugin we would get:
```js
sebmck === bar;
```
Now let's just replace the `right` property.
```js
BinaryExpression(path) {
if (path.node.operator !== "===") {
return;
}
path.node.left = t.identifier("sebmck");
path.node.right = t.identifier("dork");
}
```
And now for our final result:
```js
sebmck === dork;
```
Awesome! Our very first Babel plugin.
* * *
# Transformation Operations
## Visiting
### Get the Path of Sub-Node
To access an AST node's property you normally access the node and then the property. `path.node.property`
```js
// the BinaryExpression AST node has properties: `left`, `right`, `operator`
BinaryExpression(path) {
path.node.left;
path.node.right;
path.node.operator;
}
```
If you need to access the `path` of that property instead, use the `get` method of a path, passing in the string to the property.
```js
BinaryExpression(path) {
path.get('left');
}
Program(path) {
path.get('body.0');
}
```
### Check if a node is a certain type
If you want to check what the type of a node is, the preferred way to do so is:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left)) {
// ...
}
}
```
You can also do a shallow check for properties on that node:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
This is functionally equivalent to:
```js
BinaryExpression(path) {
if (
path.node.left != null &&
path.node.left.type === "Identifier" &&
path.node.left.name === "n"
) {
// ...
}
}
```
### Check if a path is a certain type
A path has the same methods for checking the type of a node:
```js
BinaryExpression(path) {
if (path.get('left').isIdentifier({ name: "n" })) {
// ...
}
}
```
is equivalent to doing:
```js
BinaryExpression(path) {
if (t.isIdentifier(path.node.left, { name: "n" })) {
// ...
}
}
```
### Check if an identifier is referenced
```js
Identifier(path) {
if (path.isReferencedIdentifier()) {
// ...
}
}
```
Alternatively:
```js
Identifier(path) {
if (t.isReferenced(path.node, path.parent)) {
// ...
}
}
```
### Find a specific parent path
Sometimes you will need to traverse the tree upwards from a path until a condition is satisfied.
Call the provided `callback` with the `NodePath`s of all the parents. When the `callback` returns a truthy value, we return that `NodePath`.
```js
path.findParent((path) => path.isObjectExpression());
```
If the current path should be included as well:
```js
path.find((path) => path.isObjectExpression());
```
Find the closest parent function or program:
```js
path.getFunctionParent();
```
Walk up the tree until we hit a parent node path in a list
```js
path.getStatementParent();
```
### Get Sibling Paths
If a path is in a list like in the body of a `Function`/`Program`, it will have "siblings".
* Check if a path is part of a list with `path.inList`
* You can get the surrounding siblings with `path.getSibling(index)`,
* The current path's index in the container with `path.key`,
* The path's container (an array of all sibling nodes) with `path.container`
* Get the name of the key of the list container with `path.listKey`
> These APIs are used in the [transform-merge-sibling-variables](https://github.com/babel/babili/blob/master/packages/babel-plugin-transform-merge-sibling-variables/src/index.js) plugin used in [babel-minify](https://github.com/babel/babili).
```js
var a = 1; // pathA, path.key = 0
var b = 2; // pathB, path.key = 1
var c = 3; // pathC, path.key = 2
```
```js
export default function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
// if the current path is pathA
path.inList // true
path.listKey // "body"
path.key // 0
path.getSibling(0) // pathA
path.getSibling(path.key + 1) // pathB
path.container // [pathA, pathB, pathC]
}
}
};
}
```
### Stopping Traversal
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
```js
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
```
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
`path.skip()` skips traversing the children of the current path. `path.stop()` stops traversal entirely.
```js
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
```
## Manipulation
### Replacing a node
```js
BinaryExpression(path) {
path.replaceWith(
t.binaryExpression("**", path.node.left, t.numberLiteral(2))
);
}
```
```diff
function square(n) {
- return n * n;
+ return n ** 2;
}
```
### Replacing a node with multiple nodes
```js
ReturnStatement(path) {
path.replaceWithMultiple([
t.expressionStatement(t.stringLiteral("Is this the real life?")),
t.expressionStatement(t.stringLiteral("Is this just fantasy?")),
t.expressionStatement(t.stringLiteral("(Enjoy singing the rest of the song in your head)")),
]);
}
```
```diff
function square(n) {
- return n * n;
+ "Is this the real life?";
+ "Is this just fantasy?";
+ "(Enjoy singing the rest of the song in your head)";
}
```
> **Note:** When replacing an expression with multiple nodes, they must be statements. This is because Babel uses heuristics extensively when replacing nodes which means that you can do some pretty crazy transformations that would be extremely verbose otherwise.
### Replacing a node with a source string
```js
FunctionDeclaration(path) {
path.replaceWithSourceString(`function add(a, b) {
return a + b;
}`);
}
```
```diff
- function square(n) {
- return n * n;
+ function add(a, b) {
+ return a + b;
}
```
> **Note:** It's not recommended to use this API unless you're dealing with dynamic source strings, otherwise it's more efficient to parse the code outside of the visitor.
### Inserting a sibling node
```js
FunctionDeclaration(path) {
path.insertBefore(t.expressionStatement(t.stringLiteral("Because I'm easy come, easy go.")));
path.insertAfter(t.expressionStatement(t.stringLiteral("A little high, little low.")));
}
```
```diff
+ "Because I'm easy come, easy go.";
function square(n) {
return n * n;
}
+ "A little high, little low.";
```
> **Note:** This should always be a statement or an array of statements. This uses the same heuristics mentioned in [Replacing a node with multiple nodes](#replacing-a-node-with-multiple-nodes).
### Inserting into a container
If you want to insert into a AST node property like that is an array like `body`. It is similar to `insertBefore`/`insertAfter` other than you having to specify the `listKey` which is usually `body`.
```js
ClassMethod(path) {
path.get('body').unshiftContainer('body', t.expressionStatement(t.stringLiteral('before')));
path.get('body').pushContainer('body', t.expressionStatement(t.stringLiteral('after')));
}
```
```diff
class A {
constructor() {
+ "before"
var a = 'middle';
+ "after"
}
}
```
### Removing a node
```js
FunctionDeclaration(path) {
path.remove();
}
```
```diff
- function square(n) {
- return n * n;
- }
```
### Replacing a parent
Just call `replaceWith` with the parentPath: `path.parentPath`
```js
BinaryExpression(path) {
path.parentPath.replaceWith(
t.expressionStatement(t.stringLiteral("Anyway the wind blows, doesn't really matter to me, to me."))
);
}
```
```diff
function square(n) {
- return n * n;
+ "Anyway the wind blows, doesn't really matter to me, to me.";
}
```
### Removing a parent
```js
BinaryExpression(path) {
path.parentPath.remove();
}
```
```diff
function square(n) {
- return n * n;
}
```
## Scope
### Checking if a local variable is bound
```js
FunctionDeclaration(path) {
if (path.scope.hasBinding("n")) {
// ...
}
}
```
This will walk up the scope tree and check for that particular binding.
You can also check if a scope has its **own** binding:
```js
FunctionDeclaration(path) {
if (path.scope.hasOwnBinding("n")) {
// ...
}
}
```
### Generating a UID
This will generate an identifier that doesn't collide with any locally defined variables.
```js
FunctionDeclaration(path) {
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid" }
path.scope.generateUidIdentifier("uid");
// Node { type: "Identifier", name: "_uid2" }
}
```
### Pushing a variable declaration to a parent scope
Sometimes you may want to push a `VariableDeclaration` so you can assign to it.
```js
FunctionDeclaration(path) {
const id = path.scope.generateUidIdentifierBasedOnNode(path.node.id);
path.remove();
path.scope.parent.push({ id, init: path.node });
}
```
```diff
- function square(n) {
+ var _square = function square(n) {
return n * n;
- }
+ };
```
### Rename a binding and its references
```js
FunctionDeclaration(path) {
path.scope.rename("n", "x");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(x) {
+ return x * x;
}
```
Alternatively, you can rename a binding to a generated unique identifier:
```js
FunctionDeclaration(path) {
path.scope.rename("n");
}
```
```diff
- function square(n) {
- return n * n;
+ function square(_n) {
+ return _n * _n;
}
```
* * *
# 外掛選擇
If you would like to let your users customize the behavior of your Babel plugin you can accept plugin specific options which users can specify like this:
```js
{
plugins: [
["my-plugin", {
"option1": true,
"option2": false
}]
]
}
```
These options then get passed into plugin visitors through the `state` object:
```js
export default function({ types: t }) {
return {
visitor: {
FunctionDeclaration(path, state) {
console.log(state.opts);
// { option1: true, option2: false }
}
}
}
}
```
These options are plugin-specific and you cannot access options from other plugins.
## Pre and Post in Plugins
Plugins can have functions that are run before or after plugins. They can be used for setup or cleanup/analysis purposes.
```js
export default function({ types: t }) {
return {
pre(state) {
this.cache = new Map();
},
visitor: {
StringLiteral(path) {
this.cache.set(path.node.value, 1);
}
},
post(state) {
console.log(this.cache);
}
};
}
```
## Enabling Syntax in Plugins
Plugins can enable [babylon plugins](https://github.com/babel/babylon#plugins) so that users don't need to install/enable them. This prevents a parsing error without inheriting the syntax plugin.
```js
export default function({ types: t }) {
return {
inherits: require("babel-plugin-syntax-jsx")
};
}
```
## Throwing a Syntax Error
If you want to throw an error with babel-code-frame and a message:
```js
export default function({ types: t }) {
return {
visitor: {
StringLiteral(path) {
throw path.buildCodeFrameError("Error message here");
}
}
};
}
```
The error looks like:
file.js: Error message here
7 |
8 | let tips = [
> 9 | "Click on any AST node with a '+' to expand it",
| ^
10 |
11 | "Hovering over a node highlights the \
12 | corresponding part in the source code",
* * *
# 節點構成
When writing transformations you'll often want to build up some nodes to insert into the AST. As mentioned previously, you can do this using the [builder](#builders) methods in the [`babel-types`](#babel-types) package.
The method name for a builder is simply the name of the node type you want to build except with the first letter lowercased. For example if you wanted to build a `MemberExpression` you would use `t.memberExpression(...)`.
The arguments of these builders are decided by the node definition. There's some work that's being done to generate easy-to-read documentation on the definitions, but for now they can all be found [here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions).
A node definition looks like the following:
```js
defineType("MemberExpression", {
builder: ["object", "property", "computed"],
visitor: ["object", "property"],
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
});
```
Here you can see all the information about this particular node type, including how to build it, traverse it, and validate it.
By looking at the `builder` property, you can see the 3 arguments that will be needed to call the builder method (`t.memberExpression`).
```js
builder: ["object", "property", "computed"],
```
> Note that sometimes there are more properties that you can customize on the node than the `builder` array contains. This is to keep the builder from having too many arguments. In these cases you need to set the properties manually. An example of this is [`ClassMethod`](https://github.com/babel/babel/blob/bbd14f88c4eea88fa584dd877759dd6b900bf35e/packages/babel-types/src/definitions/es2015.js#L238-L276).
```js
// Example
// because the builder doesn't contain `async` as a property
var node = t.classMethod(
"constructor",
t.identifier("constructor"),
params,
body
)
// set it manually after creation
node.async = true;
```
You can see the validation for the builder arguments with the `fields` object.
```js
fields: {
object: {
validate: assertNodeType("Expression")
},
property: {
validate(node, key, val) {
let expectedType = node.computed ? "Expression" : "Identifier";
assertNodeType(expectedType)(node, key, val);
}
},
computed: {
default: false
}
}
```
You can see that `object` needs to be an `Expression`, `property` either needs to be an `Expression` or an `Identifier` depending on if the member expression is `computed` or not and `computed` is simply a boolean that defaults to `false`.
So we can construct a `MemberExpression` by doing the following:
```js
t.memberExpression(
t.identifier('object'),
t.identifier('property')
// `computed` is optional
);
```
Which will result in:
```js
object.property
```
However, we said that `object` needed to be an `Expression` so why is `Identifier` valid?
Well if we look at the definition of `Identifier` we can see that it has an `aliases` property which states that it is also an expression.
```js
aliases: ["Expression", "LVal"],
```
So since `MemberExpression` is a type of `Expression`, we could set it as the `object` of another `MemberExpression`:
```js
t.memberExpression(
t.memberExpression(
t.identifier('member'),
t.identifier('expression')
),
t.identifier('property')
)
```
Which will result in:
```js
member.expression.property
```
It's very unlikely that you will ever memorize the builder method signatures for every node type. So you should take some time and understand how they are generated from the node definitions.
You can find all of the actual [definitions here](https://github.com/babel/babel/tree/master/packages/babel-types/src/definitions) and you can see them [documented here](https://github.com/babel/babel/blob/master/doc/ast/spec.md)
* * *
# Best Practices
## Create Helper Builders and Checkers
It's pretty simple to extract certain checks (if a node is a certain type) into their own helper functions as well as extracting out helpers for specific node types.
```js
function isAssignment(node) {
return node && node.operator === opts.operator + "=";
}
function buildAssignment(left, right) {
return t.assignmentExpression("=", left, right);
}
```
## Avoid traversing the AST as much as possible
Traversing the AST is expensive, and it's easy to accidentally traverse the AST more than necessary. This could be thousands if not tens of thousands of extra operations.
Babel optimizes this as much as possible, merging visitors together if it can in order to do everything in a single traversal.
### Merge visitors whenever possible
When writing visitors, it may be tempting to call `path.traverse` in multiple places where they are logically necessary.
```js
path.traverse({
Identifier(path) {
// ...
}
});
path.traverse({
BinaryExpression(path) {
// ...
}
});
```
However, it is far better to write these as a single visitor that only gets run once. Otherwise you are traversing the same tree multiple times for no reason.
```js
path.traverse({
Identifier(path) {
// ...
},
BinaryExpression(path) {
// ...
}
});
```
### Do not traverse when manual lookup will do
It may also be tempting to call `path.traverse` when looking for a particular node type.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.get('params').traverse(nestedVisitor);
}
};
```
However, if you are looking for something specific and shallow, there is a good chance you can manually lookup the nodes you need without performing a costly traversal.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.node.params.forEach(function() {
// ...
});
}
};
```
## Optimizing nested visitors
When you are nesting visitors, it might make sense to write them nested in your code.
```js
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse({
Identifier(path) {
// ...
}
});
}
};
```
However, this creates a new visitor object every time `FunctionDeclaration()` is called. That can be costly, because Babel does some processing each time a new visitor object is passed in (such as exploding keys containing multiple types, performing validation, and adjusting the object structure). Because Babel stores flags on visitor objects indicating that it's already performed that processing, it's better to store the visitor in a variable and pass the same object each time.
```js
const nestedVisitor = {
Identifier(path) {
// ...
}
};
const MyVisitor = {
FunctionDeclaration(path) {
path.traverse(nestedVisitor);
}
};
```
If you need some state within the nested visitor, like so:
```js
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse({
Identifier(path) {
if (path.node.name === exampleState) {
// ...
}
}
});
}
};
```
You can pass it in as state to the `traverse()` method and have access to it on `this` in the visitor.
```js
const nestedVisitor = {
Identifier(path) {
if (path.node.name === this.exampleState) {
// ...
}
}
};
const MyVisitor = {
FunctionDeclaration(path) {
var exampleState = path.node.params[0].name;
path.traverse(nestedVisitor, { exampleState });
}
};
```
## Being aware of nested structures
Sometimes when thinking about a given transform, you might forget that the given structure can be nested.
For example, imagine we want to lookup the `constructor` `ClassMethod` from the `Foo` `ClassDeclaration`.
```js
class Foo {
constructor() {
// ...
}
}
```
```js
const constructorVisitor = {
ClassMethod(path) {
if (path.node.name === 'constructor') {
// ...
}
}
}
const MyVisitor = {
ClassDeclaration(path) {
if (path.node.id.name === 'Foo') {
path.traverse(constructorVisitor);
}
}
}
```
We are ignoring the fact that classes can be nested and using the traversal above we will hit a nested `constructor` as well:
```js
class Foo {
constructor() {
class Bar {
constructor() {
// ...
}
}
}
}
```
## Unit Testing
There are a few primary ways to test babel plugins: snapshot tests, AST tests, and exec tests. We'll use [jest](http://facebook.github.io/jest/) for this example because it supports snapshot testing out of the box. The example we're creating here is hosted in [this repo](https://github.com/brigand/babel-plugin-testing-example).
First we need a babel plugin, we'll put this in src/index.js.
```js
module.exports = function testPlugin(babel) {
return {
visitor: {
Identifier(path) {
if (path.node.name === 'foo') {
path.node.name = 'bar';
}
}
}
};
};
```
### Snapshot Tests
Next, install our dependencies with `npm install --save-dev babel-core jest`, and then we can begin writing our first test: the snapshot. Snapshot tests allow us to visually inspect the output of our babel plugin. We give it an input, tell it to make a snapshot, and it saves it to a file. We check in the snapshots into git. This allows us to see when we've affected the output of any of our test cases. It also gives use a diff in pull requests. Of course you could do this with any test framework, but with jest updating the snapshots is as easy as `jest -u`.
```js
// src/__tests__/index-test.js
const babel = require('babel-core');
const plugin = require('../');
var example = `
var foo = 1;
if (foo) console.log(foo);
`;
it('works', () => {
const {code} = babel.transform(example, {plugins: [plugin]});
expect(code).toMatchSnapshot();
});
```
This gives us a snapshot file in `src/__tests__/__snapshots__/index-test.js.snap`.
```js
exports[`test works 1`] = `
"
var bar = 1;
if (bar) console.log(bar);"
`;
```
If we change 'bar' to 'baz' in our plugin and run jest again, we get this:
```diff
Received value does not match stored snapshot 1.
- Snapshot
+ Received
@@ -1,3 +1,3 @@
"
-var bar = 1;
-if (bar) console.log(bar);"
+var baz = 1;
+if (baz) console.log(baz);"
```
We see how our change to the plugin code affected the output of our plugin, and if the output looks good to us, we can run `jest -u` to update the snapshot.
### AST Tests
In addition to snapshot testing, we can manually inspect the AST. This is a simple but brittle example. For more involved situations you may wish to leverage babel-traverse. It allows you to specify an object with a `visitor` key, exactly like you use for the plugin itself.
```js
it('contains baz', () => {
const {ast} = babel.transform(example, {plugins: [plugin]});
const program = ast.program;
const declaration = program.body[0].declarations[0];
assert.equal(declaration.id.name, 'baz');
// or babelTraverse(program, {visitor: ...})
});
```
### Exec Tests
Here we'll be transforming the code, and then evaluating that it behaves correctly. Note that we're not using `assert` in the test. This ensures that if our plugin does weird stuff like removing the assert line by accident, the test will still fail.
```js
it('foo is an alias to baz', () => {
var input = `
var foo = 1;
// test that foo was renamed to baz
var res = baz;
`;
var {code} = babel.transform(input, {plugins: [plugin]});
var f = new Function(`
${code};
return res;
`);
var res = f();
assert(res === 1, 'res is 1');
});
```
Babel core uses a [similar approach](https://github.com/babel/babel/blob/7.0/CONTRIBUTING.md#writing-tests) to snapshot and exec tests.
### [`babel-plugin-tester`](https://github.com/kentcdodds/babel-plugin-tester)
This package makes testing plugins easier. If you're familiar with ESLint's [RuleTester](http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests) this should be familiar. You can look at [the docs](https://github.com/kentcdodds/babel-plugin-tester/blob/master/README.md) to get a full sense of what's possible, but here's a simple example:
```js
import pluginTester from 'babel-plugin-tester';
import identifierReversePlugin from '../identifier-reverse-plugin';
pluginTester({
plugin: identifierReversePlugin,
fixtures: path.join(__dirname, '__fixtures__'),
tests: {
'does not change code with no identifiers': '"hello";',
'changes this code': {
code: 'var hello = "hi";',
output: 'var olleh = "hi";',
},
'using fixtures files': {
fixture: 'changed.js',
outputFixture: 'changed-output.js',
},
'using jest snapshots': {
code: `
function sayHi(person) {
return 'Hello ' + person + '!'
}
`,
snapshot: true,
},
},
});
```
* * *
> ***For future updates, follow [@thejameskyle](https://twitter.com/thejameskyle) and [@babeljs](https://twitter.com/babeljs) on Twitter.***
================================================
FILE: translations/zh-Hant/user-handbook.md
================================================
# Babel 使用手冊
本文件包含了[Babel](https://babeljs.io)及其相關工具的所有資訊。
[](http://creativecommons.org/licenses/by/4.0/)
本使用手冊也提供了其它語系的版本,請參閱[讀我檔案](/README.md)以獲得完整清單。
# 目錄
* [簡介](#toc-introduction)
* [安裝 Babel](#toc-setting-up-babel)
* [`babel-cli`](#toc-babel-cli)
* [在專案底下使用 Babel CLI](#toc-running-babel-cli-from-within-a-project)
* [`babel-register`](#toc-babel-register)
* [`babel-node`](#toc-babel-node)
* [`babel-core`](#toc-babel-core)
* [設定 Babel](#toc-configuring-babel)
* [`.babelrc`](#toc-babelrc)
* [`babel-preset-es2015`](#toc-babel-preset-es2015)
* [`babel-preset-react`](#toc-babel-preset-react)
* [`babel-preset-stage-x`](#toc-babel-preset-stage-x)
* [執行已轉換的程式碼](#toc-executing-babel-generated-code)
* [`babel-polyfill`](#toc-babel-polyfill)
* [`babel-runtime`](#toc-babel-runtime)
* [Babel 進階設定](#toc-configuring-babel-advanced)
* [手動指定外掛](#toc-manually-specifying-plugins)
* [外掛選項](#toc-plugin-options)
* [基於環境的 Babel 客製化](#toc-customizing-babel-based-on-environment)
* [撰寫自己的 preset](#toc-making-your-own-preset)
* [其他工具](#toc-babel-and-other-tools)
* [靜態分析工具](#toc-static-analysis-tools)
* [Linting](#toc-linting)
* [Code Style](#toc-code-style)
* [撰寫說明文件](#toc-documentation)
* [Frameworks](#toc-frameworks)
* [React](#toc-react)
* [IDE 及編輯器](#toc-text-editors-and-ides)
* [技術支援](#toc-babel-support)
* [Babel Forum](#toc-babel-forum)
* [Babel Chat](#toc-babel-chat)
* [Babel Issues](#toc-babel-issues)
* [Creating an awesome Babel bug report](#toc-creating-an-awesome-babel-bug-report)
# 簡介
Babel 是個 JavaScript 通用型多功能編譯器。藉由 Babel ,你可享受到(或創建出)新世代的 JavaScript 及功能。
JavaScript 作為一種不斷演進的程式語言,新功能的規格制定和提議不斷推陳出新。 使用 Babel 可讓你在這些新功能廣為普及之前就先行上手。
Babel 靠的就是把你依據最新標準所寫下的程式碼編譯成時下最普及版本。 這種程序叫程式碼對程式碼編譯(source-to-source compiling),亦稱之為「轉譯」(transpiling)。
舉例來說,Babel 能將 ES2015 的新語法,arrow function:
```js
const square = n => n * n;
```
轉譯成:
```js
const square = function square(n) {
return n * n;
};
```
不過 Babel 能做的不僅如此,它能支援擴充語法,例如能支援 React 的 JSX 語法,以及能進行靜態型別檢查的 Flow 語法。
而且在 Babel 裡,一切都是以外掛的形式存在。任何人都可創作自己的外掛,並藉由 Babel 的威力來做任何事。
*甚至更進一步地*,Babel 自身被解構成數個核心模組,任何人都可用它們來打造新一代的 JavaScript 工具。
目前已有許多人這麼做了,整個環繞著 Babel 的生態圈蓬勃發展,充滿著多樣性。 在這份手冊裡,我會談到如何被用 Babel 的內建工具,以及這社群裡的一些實用的東西。
> ***進一步的最新資訊,請追蹤[@thejameskyle](https://twitter.com/thejameskyle)的 Twitter 帳號。***
* * *
# 安裝 Babel
由於 JavaScript 社群裡並沒有統一的建置工具、程式架構、或平臺之類的,Babel 對於所有主流工具整合都有正式的支援。 從 Gulp 到 Browserify、從 Ember 到 Meteor,無論你的建置環境如何設置,Babel 可能都正式整合進來了。
基於本手冊的宗旨,我們只會提到 Babel 內建的安裝方式。不過您可參閱互動式的[安裝頁面](http://babeljs.io/docs/setup)來得知其他整合方式的詳情。
> **注意:**本指南會提到一些命令列工具,例如:`node` 和 `npm`。在閱讀下去之前,請確保您對這些工具有夠的了解。
## `babel-cli`
Babel 的命令列介面(CLI)是編譯檔案最簡單的方法。
我們先以全域安裝的方式來學些基本的
```sh
$ npm install --global babel-cli
```
我們可以像這樣來編譯我們的第一個檔案了:
```sh
$ babel my-file.js
```
這樣會把編譯結果直接輸出到你的終端機上。要讓它輸出到檔案,我們得指定:`--out-file` 或 `-o`.
```sh
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
```
如果我們想編譯整個目錄內的檔案,並把結果輸出到另一個目錄下,我們可以使用:`--out-dir` 或 `-d`.
```sh
$ babel src --out-dir lib
# or
$ babel src -d lib
```
### 在專案下執行 Babel CLI
雖然您*能*在把 Babel 安裝在機器的全域之下,但在各個專案之下,進行**本地**安裝會更適合。
這麼做有兩個主要原因:
1. 同一臺機器上的兩個專案,各自使用自己的 Babel,能讓您進行個別的更動。
2. 如此意謂著你的工作環境不會與非必要的東西產生隱性相依,讓你的專案更具可攜性,更容易設置。
要進行本地安裝 Babel 命令列,可執行:
```sh
$ npm install --save-dev babel-cli
```
> **注意:**既然一般而言,在全域下執行 Babel 不是個好做法。您或許會想解除安裝全域下的 Babel,請執行:
>
> ```sh
$ npm uninstall --global babel-cli
```
安裝完成後,你的 `package.json` 檔看起來應該像這樣:
```json
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
現在,我們不是直接在命令列下執行 Babel,而是把我們的命令放進 **npm scripts** 裡,使用本地版本的 Babel。
只要在你的 `package.json` 檔裡加入 `"scripts"` 欄位,並把對 Babel 的命令放在 `build` 之後。.
```diff
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
```
現在,我們可以從終端機執行:
```js
npm run build
```
這樣就能跟之前一樣地執行 Babel 了,只不過使用的是本地的版本。
## `babel-register`
下個經常使用 Babel 的方式是透過 `babel-register`。這種方式,只要有所需的檔案就能執行 Babel 了,這樣或許能跟你的設置做出更好的整合。
但是請注意這並不適合在正式環境中使用。 部署以此法編譯出的程式並不是好做法。 在部署之前,先把程式編譯好才比較好。 However this works quite well for build scripts or other things that you run locally.
首先,我們在專案中建立一個 `index.js` 檔。
```js
console.log("Hello world!");
```
如果我們執行 `node index.js`,Babel 不會編譯它。我們得安裝 `babel-register`.
首先,安裝 `babel-register`.
```sh
$ npm install --save-dev babel-register
```
接著在專案中建立一個 `register.js` 檔,並在其中加入下列程式碼:
```js
require("babel-register");
require("./index.js");
```
這麼做是在 Node 的模組系統中*注冊*Babel,並且開始編譯所有被 `require` 的檔案。
現在,我們可以用 `register.js` 取代 `node index.js` 了。
```sh
$ node register.js
```
> **注意:**您不能在想編譯的檔案中注冊 Babel。因為 node 會在 Babel 編譯它前就先執行它。
>
> ```js
require("babel-register");
// not compiled:
console.log("Hello world!");
```
## `babel-node`
如果您只是透過 `node` 命令列介面來執行某些程式,整合 Babel 的最簡單方式可能就是使用 `babel-node` 命令列介面了。大體上而言,它可算是 `node` 命令列介面的替代品。
但是請注意這並不適合在正式環境中使用。 部署以此法編譯出的程式並不是好做法。 在部署之前,先把程式編譯好才比較好。 However this works quite well for build scripts or other things that you run locally.
首先,請確定您己安裝了 `babel-cli`
```sh
$ npm install --save-dev babel-cli
```
> **Note:** If you are wondering why we are installing this locally, please read the [Running Babel CLI from within a project](#toc-running-babel-cli-from-within-a-project) section above.
然後在任何您執行 `node` 的地方,以 `babel-node` 取代之。.
如果您使用 npm `scripts`,您只需:
```diff
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
```
不然的話,你得指明 `babel-node` 的完整路徑。
```diff
- node script.js
+ ./node_modules/.bin/babel-node script.js
```
> 提示:你也可使用 [`npm-run`](https://www.npmjs.com/package/npm-run).
## `babel-core`
如果您由於某種原因,需要使用程式控制 Babel,您可直接使用 `babel-core` 套件。
首先安裝 `babel-core`.
```sh
$ npm install babel-core
```
```js
var babel = require("babel-core");
```
如果您的 JavaScript 是字串形式的,您可直接使用 `babel.transform` 編譯它。.
```js
babel.transform("code();", options);
// => { code, map, ast }
```
如果是檔案形式的,您可選擇使用非同步 API:
```js
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
```
或是同步 API:
```js
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
```
無論如何,如果您已經有了 Babel 的抽象語法樹(AST),您也可直接將之編譯。
```js
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
```
For all of the above methods, `options` refers to https://babeljs.io/docs/usage/api/#options.
* * *
# 設定 Babel
現在你可能注意到了,單獨執行 Babel 看來除了把 JavaScript 檔案從一處複製到另一處之外,並沒什麼其他的作用。
這是因為我們還沒告訴 Babel 要做什麼。
> 因為 Babel 是個通用型的編譯器,有各式各樣的使用方式。在預設的情況下,它不會做任何事。你得明確告訴 Babel 該做些什麼。
您可藉由安裝 **外掛程式** 或 **presets**(一組外掛程式)來指示 Babel 該做些什麼。
## `.babelrc`
在我們告訴 Babel 做什麼之前,我們需要建立一個組態檔。我們需要做的只是建立一個 `.babelrc` 檔,把它放在您的專案的根目錄。剛開始它的內容就像這樣:
```js
{
"presets": [],
"plugins": []
}
```
這個檔案就是您用來設定 Babel,讓它執行您想做的事。
> **注意:**儘管您還是有其他方式可把各種選項傳給 Babel,但 `.babelrc` 檔仍是最常規、最好的方式。
## `babel-preset-es2015`
我們就先從叫 Babel 把 ES2015(最新版的 JavaScrpt 標準,亦稱 ES6)程式碼編譯成 ES5(大多數 JavaScript 環境都支援的版本)程式碼。
我們得安裝「ES2015」Babel preset:
```sh
$ npm install --save-dev babel-preset-es2015
```
接著,我們得修改 `.babelrc` 檔來把那 preset 包含進來。
```diff
{
"presets": [
+ "es2015"
],
"plugins": []
}
```
## `babel-preset-react`
要安裝 React 很容易,只需安裝 preset:
```sh
$ npm install --save-dev babel-preset-react
```
然後把那 preset 加入您的 `.babelrc` 檔即可:
```diff
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
```
## `babel-preset-stage-x`
JavaScript 還有些提議案正經過 TC39(ECMAScript 標準背後的技術委員會)的審議程序,準備加入標準。
這個程序被分為五個階段(0-4)。 提案獲得越多關注就越有可能被採納。它們通過各個階段審議,最後在階段4被接受並納入標準。
這些提案被包裹成四個不同的 preset:
* `babel-preset-stage-0`
* `babel-preset-stage-1`
* `babel-preset-stage-2`
* `babel-preset-stage-3`
> 注意階段4的 preset 並不存在,因為它就是上面提到的 `es2015` preset
上述各階段 preset 都相依於它下個階段的 preset,例如:`babel-preset-stage-1` 相依於 `babel-preset-stage-2`,而後者又相依於 `babel-preset-stage-3`.
要安裝您感興趣的階段只需:
```sh
$ npm install --save-dev babel-preset-stage-2
```
然後您就可把它加入您的 `.babelrc` 組態檔.
```diff
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
```
* * *
# 執行已轉換的程式碼
So you've compiled your code with Babel, but this is not the end of the story.
## `babel-polyfill`
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
```js
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
```
Which turns into this:
```js
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
```
However, this still won't work everywhere because `Array.from` doesn't exist in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a [Polyfill](https://remysharp.com/2010/10/08/what-is-a-polyfill). Simply put, a polyfill is a piece of code that replicates a native api that does not exist in the current runtime. Allowing you to use APIs such as `Array.from` before they are available.
Babel uses the excellent [core-js](https://github.com/zloirock/core-js) as its polyfill, along with a customized [regenerator](https://github.com/facebook/regenerator) runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
```sh
$ npm install --save babel-polyfill
```
Then simply include the polyfill at the top of any file that requires it:
```js
import "babel-polyfill";
```
## `babel-runtime`
In order to implement details of ECMAScript specs, Babel will use "helper" methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single "runtime" which gets required.
Start by installing `babel-plugin-transform-runtime` and `babel-runtime`:
```sh
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
```
Then update your `.babelrc`:
```diff
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
```
Now Babel will compile code like the following:
```js
class Foo {
method() {}
}
```
Into this:
```js
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
```
Rather than putting the `_classCallCheck` and `_createClass` helpers in every single file where they are needed.
* * *
# Babel 進階設定
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
## 手動指定外掛
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
```sh
$ npm install --save-dev babel-plugin-transform-es2015-classes
```
Then add the `plugins` field to your `.babelrc`.
```diff
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
```
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the [Babel Plugins page](http://babeljs.io/docs/plugins/).
Also take a look at all the plugins that have been [built by the community](https://www.npmjs.com/search?q=babel-plugin). If you would like to learn how to write your own plugin read the [Babel Plugin Handbook](plugin-handbook.md).
## 外掛選項
Many plugins also have options to configure them to behave differently. For example, many transforms have a "loose" mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
```diff
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
```
> I'll be working on updates to the plugin documentation to detail every option in the coming weeks. [Follow me for updates](https://twitter.com/thejameskyle).
## 基於環境的 Babel 客製化
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the environment. You can do this easily with your `.babelrc` file.
```diff
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
```
Babel will enable configuration inside of `env` based on the current environment.
The current environment will use `process.env.BABEL_ENV`. When `BABEL_ENV` is not available, it will fallback to `NODE_ENV`, and if that is not available it will default to `"development"`.
**Unix**
```sh
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
```
**Windows**
```sh
$ SET BABEL_ENV=production
$ [COMMAND]
```
> **Note:** `[COMMAND]` is whatever you use to run Babel (ie. `babel`, `babel-node`, or maybe just `node` if you are using the register hook).
>
> **Tip:** If you want your command to work across unix and windows platforms then use [`cross-env`](https://www.npmjs.com/package/cross-env).
## 撰寫自己的 preset
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific [node version](https://github.com/leebenson/babel-preset-node5) you are running, or maybe a preset for your [entire](https://github.com/cloudflare/babel-preset-cf) [company](https://github.com/airbnb/babel-preset-airbnb).
It's easy to create a preset. Say you have this `.babelrc` file:
```js
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
```
All you need to do is create a new project following the naming convention `babel-preset-*` (please be responsible with this namespace), and create two files.
First, create a new `package.json` file with the necessary `dependencies` for your preset.
```js
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle ",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
```
Then create an `index.js` file that exports the contents of your `.babelrc` file, replacing plugin/preset strings with `require` calls.
```js
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
```
Then simply publish this to npm and you can use it like you would any preset.
* * *
# 其他工具
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
## 靜態分析工具
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
### Linting
One of the most popular tools for linting is [ESLint](http://eslint.org), because of this we maintain an official [`babel-eslint`](https://github.com/babel/babel-eslint) integration.
First install `eslint` and `babel-eslint`.
```sh
$ npm install --save-dev eslint babel-eslint
```
Next create or use the existing `.eslintrc` file in your project and set the `parser` as `babel-eslint`.
```diff
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
```
Now add a `lint` task to your npm `package.json` scripts:
```diff
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
```
Then just run the task and you will be all setup.
```sh
$ npm run lint
```
For more information consult the [`babel-eslint`](https://github.com/babel/babel-eslint) or [`eslint`](http://eslint.org) documentation.
### Code Style
> JSCS has merged with ESLint, so checkout Code Styling with ESLint.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects ([@hzoo](https://github.com/hzoo)) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the `--esnext` option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the `esnext` option to your `.jscsrc` file.
```diff
{
"preset": "airbnb",
+ "esnext": true
}
```
For more information consult the [`babel-jscs`](https://github.com/jscs-dev/babel-jscs) or [`jscs`](http://jscs.info) documentation.
### 撰寫說明文件
Using Babel, ES2015, and Flow you can infer a lot about your code. Using [documentation.js](http://documentation.js.org) you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
## Frameworks
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users' experience.
### React
React has dramatically changed their API to align with ES2015 classes ([Read about the updated API here](https://babeljs.io/blog/2015/06/07/react-on-es6-plus)). Even further, React relies on Babel to compile it's JSX syntax, deprecating it's own custom tooling in favor of Babel. You can start by setting up the `babel-preset-react` package following the [instructions above](#babel-preset-react).
The React community took Babel and ran with it. There are now a number of transforms [built by the community](https://www.npmjs.com/search?q=babel-plugin+react).
Most notably the [`babel-plugin-react-transform`](https://github.com/gaearon/babel-plugin-react-transform) plugin which combined with a number of [React-specific transforms](https://github.com/gaearon/babel-plugin-react-transform#transforms) can enable things like *hot module reloading* and other debugging utilities.
## IDE 及編輯器
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn't support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
* [Sublime Text](https://github.com/babel/babel-sublime)
* [Atom](https://atom.io/packages/language-babel)
* [Vim](https://github.com/jbgutierrez/vim-babel)
* [WebStorm](https://babeljs.io/docs/setup/#webstorm)
* * *
# 技術支援
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a [Code of Conduct](https://github.com/babel/babel/blob/master/CODE_OF_CONDUCT.md). If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
## Babel Forum
[Discourse](http://www.discourse.org) has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by [discuss.babeljs.io](https://discuss.babeljs.io).
## Babel Chat
Everyone loves [Slack](https://slack.com). If you're looking for immediate support from the community then come chat with us at [slack.babeljs.io](https://slack.babeljs.io).
## Babel Issues
Babel uses the issue tracker provided by [Github](http://github.com).
You can see all the open and closed issues on [Github](https://github.com/babel/babel/issues).
If you want to open a new issue:
* [Search for an existing issue](https://github.com/babel/babel/issues)
* [Create a new bug report](https://github.com/babel/babel/issues/new) or [request a new feature](https://github.com/babel/babel/issues/new)
### Creating an awesome Babel bug report
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It's extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
> [WIP]
* * *
> ***進一步的最新資訊,請追蹤[@thejameskyle](https://twitter.com/thejameskyle)的 Twitter 帳號。***