How to use ES6 in Drupal 8

Wed, 08/17/2016 - 04:58 | 795 Views

I bet many of you doesn't understand what is ES6, being honest I didn't know this term until a few days ago. 

ES6 stands for ECMAScript v6 or ES2015, but in simple words, is just Javascript; Correct Javascript as simple as that.

So, ES6 one of the latest version of Javascript specification, but is not supported by all browsers, because was released in June 2015, even worst in June 2016 we got ES7 and as you can imagine not supported yet.

Right now all browsers support ES5, then, if it's not compatible with all browsers, why ES6+ is a big thing? Because, now Javascript is more OOP, using classes, constants, and modules, enabling to create more complex and organized projects.

So, how we could use ES6 in our web projects like Drupal, to avoid to have a particular version of our site based on our client's browser? The solution is Babel

Babel transforms your code to ES5 to be used in your web applications. 

Babel works using plugins that allow the transformation and enable to use a particular plugin based on the source of your code like React that requires an especial plugin to do the conversion.

Let me show an example that creates a simple ReactJS.

1. Install Node.js

The first thing we need to do is install Node.js in your system, the easy way to do that is downloading the proper installer for our platform from https://nodejs.org/en/download

We need to install Node.js because we are going to use the NPM which is a package manager for javascript libraries 

2.  Modify gitignore file

Drupal 8 projects include their own .gitignore file, but because we are about to include npm in our development process, we need to add some extra rules listed below:

# Node.js
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history


3. Create a package.json file

To be able to distribute later your javascript library you need to create a package.json file, you create that file in interactive mode using the command:

$ npm init

In the end, you will get a file similar to the following file.

{
  "name": "drupalreactform",
  "version": "1.0.0",
  "description": "ReactJS form to be embed in Drupal 8 Controller",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  }
  "author": "enzo - Eduardo Garcia",
  "license": "ISC"
}

4. Install Webpack

To expose our library in Drupal we need to create a distribution package, for that purpose, we will use [Webpack](webpack.github.io) that is a module bundler which takes modules with dependencies to generates static assets by bundling them together.

$ npm install webpack --save

The command about will install webpack with all required libraries and modify our package.json, in the same way that we use in Composer the composer.json file.

5. Configuring Webpack & creating bundle

It's necessary to inform to Webpack using file webpack.config.js, how we can to create our bundle to be used Drupal, the following configuration assumes we have a custom module located in web/modules/custom/mysearch.

var webpack = require('webpack');
var path = require('path');

var MODULE_BUILD_DIR = path.resolve(__dirname, 'web/modules/custom/mysearch/js');
var MODULE_APP_DIR = path.resolve(__dirname, 'web/modules/custom/mysearch/js/es6');

var config = {
  entry: MODULE_APP_DIR + '/mysearch.form.jsx',
  output: {
    path: MODULE_BUILD_DIR,
    filename: 'mysearch.form.js'
  }
};

module.exports = config;

With the configuration above, we are saying we will load the file mysearch.form.jsx and all included files in mysearch.form.js file

If you write something simple like

console.log('Hello ES6!');

You don't need any special transformation and you can create the bundle, for that propose you need to execute the following command:

$ ./node_modules/.bin/webpack -d

You will get an output similar to this image:

Webpack generation

The generation will work correctly because the source in ES5 and the output too; So, no transformation was required.

6. Testing transformation

I know I said we would embed the file generated in Drupal, but in a development process is faster if we could test first outside Drupal, for that, we could create a file named test/es62es5/index.htm inside module directory  with the following content.

<html>
  <head>
    <meta charset="utf-8">
    <title>Testing transformation ES6 -> ES5</title>
  </head>
  <body>
    <div id="app" />
    <script src="../js/mysearch.form.js" type="text/javascript"></script>
  </body>
</html>

Opening that file in our browser would enable any possible error and reduce the change to blame Drupal 8 for a malfunction in our code.

7. Use Babel to transform ES6

Now we need to install Babel and Babel loaders to be able to transform our ReactJS form into ES5; the next command installs the required packages.

$ npm install babel-loader babel-preset-es2015 babel-preset-react --save

Also, we need to create a .babelrc file, to inform to Babel what presents will be used in transformation, check and example of that file below:

{
  "presets" : ["es2015", "react"]
}

Finally, we need to modify out webpack configuration to report what loader we are going to use in our transformation, the new aspect of webpack.config.js will be like this:

var webpack = require('webpack');
var path = require('path');

var MODULE_BUILD_DIR = path.resolve(__dirname, 'web/modules/custom/mysearch/js');
var MODULE_APP_DIR = path.resolve(__dirname, 'web/modules/custom/mysearch/js/es6');

var config = {
    entry: MODULE_APP_DIR + '/mysearch.form.jsx',
    output: {
        path: MODULE_BUILD_DIR,
        filename: 'mysearch.form.js'
    },
    module : {
        loaders : [
            {
                test : /\.jsx?/,
                include : MODULE_APP_DIR,
                loader : 'babel'
            }
        ]
    }
};

8. Create React form

Before to create the form we need to install some libraries to build our form.

$ npm install react react-dom antd --save

If we plan to embed CSS or LESS files in our app, need to install loaders for that using the following instructions, and register the loader in webpack

$ npm install css-loader less less-loader style-loader  --save-dev

The code of our form will be an example form React created in a previous blog post:

import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';

var Search = React.createClass({

    render: function(){
        return (
            React.createElement('form', {onSubmit: this.onSubmit, className: 'SearchForm', noValidate: true},
                React.createElement('input', {
                    type: 'text',
                    placeholder: 'Search'
                }),
                React.createElement("select", { placeholder: 'Category', value: '', onChange: this.changeHandler },
                    React.createElement("option", { value: 1 }, "Software"),
                    React.createElement("option", { value: 2 }, "Movie")
                ),
                React.createElement('button', {type: 'submit'}, "Go")
            )
        );
    },
});

ReactDOM.render(React.createElement(Search),  document.getElementById("app"));

Of course, you can create a more advanced form, importing other libraries.

9. Include form in a Drupal controller

After "compile" our form in one file, the remaining step is to include it in a Drupal Controller, to do that you just need to follow the blog entry www.anexusit.com/blog/how-to-load-js-and-css-libraries-a-drupal-8-controller.

I hope did you find this blog entry useful.