The ES6 Development Scheme For React Using GNU Make
ES6 is so much better than current JavaScript that I couldn’t write any line of JavaScript if not using ES6. The problem is that, main stream browser and even most recently version of NodeJS(!) couldn’t support it.
Yes, there will be so much work to be done to let these engine support ES6, but since most JavaScript developers are also as eager as me.
So they build Shim and Polyfill for it. But what about the syntax change of the ES6?
This is a little complicated, since current JavaScript engine won’t accept the new syntax, so there must be some kind of translation or compilation.
And there is, Babel is one of them, and the one I used most in my workflow (and there is another reason that I use Babel as the compiler, the reason is that Babel supports React’s JSX officially, I’ll talk about that later).
The Problems To Face When Using ES6
It is not easy to choose the way of using ES6 as the main language when developing JavaScript application. You’ll get many problems to face, just list as below:
- Every out file must be compiled, or you won’t get your application run on any browser or nodejs
- Since all the output file are compiled, you’ll face a problem of C, you can’t debug the running code, unless you have some kind of debug data exists, because the result code is quite different(compiled and optimised) than the original one
- There is no official JavaScript dependency mangement(for browser), so you must choose one(for example bower, or npm), and of course, they’ll have nothing ES6(since they are JavaScript running on current engines)
- For these files, including them or tracking the change is quite complex
The Solution
For Babel, it can compile the ES6 file and create an Source Map and using main stream browsers(Firefox or Chrome) to debug the original code (yeah!).
And let’s face the problem again:
- Must compile source to dest code
- Must compile and create source map file/data
- Files are stored as trees in directory, must track every file’s change so that, you don’t rebuild the whole directory again when only change a small file
What development scheme do you recall? It’s C!
For C development, we use Makefile and Bash to to that, and it works perfectly(Yes, I know there are plenty of them using Grunt, but GNU Make and Bash can do a lot better than that).
But how about the problem 3?
For C, it is quite easy, since every file is compied as an object file .o, there will be another process of building, it is called linking.
And is there any tool in JavasScript support linking? Yes, the tool I used is Browserify.
So, the solution that will solve the problem above, should be something like this (exactly same as C, I’ll add the C part as comparation):
- Compile the code and generate the debug information (Babel – GCC)
- Tracking the file change and do the incremental compile (Makefile)
- Complex test and directory operations (Bash)
- Linking the output file and required libraries together to the product (Browserify – Link)
And Beyond That
And the way beyond is that you can make the style files be compiled too(I use Sass to compile the style code)
Even more, you can add the phase of Uglifying JavaScript and Css and the deployment phase to your make file as well.
The Details
First, you must have a GNU Make installed on your system.
This should be quite easy, you can use any package manager to install it(including Cygwin), the version that I installed using mac ports, is GNU Make 3.81.
Then you’ll need to setup the compiling environments:
- For ES6 compilation, you’ll need
- NodeJS: This is the JavaScript runtime based on Google’s A8, and yes, this is also platform independent, you can install this in almost all the morden OS
- NPM: The Package manager of Node, it is bundled with NodeJS’s installation, so you won’t need to install it.
- Babel: The ES6 compiler that I used, this can be installed just using npm like this:
npm install babel
- For the result linking you’ll need:
- Browserify: The tool that used to package all the JavaScript dependencies together and make them a single JavaScript file(I’ll talk about the benifits and cons of single JavaScript file and browserify in another post), you can install browserify just by using this command
npm install browserify
- Browserify: The tool that used to package all the JavaScript dependencies together and make them a single JavaScript file(I’ll talk about the benifits and cons of single JavaScript file and browserify in another post), you can install browserify just by using this command
And there you go, you can make GNU make to build your project.
The Make patterns
The first thing for compile is to add the compile patterns to do the compliation(just like C, source files are using file extension .c and the output files are using file extension .o).
I’m using this scheme to init the pattern:
- The dependencies are using NPM to do the management, so, all the dependencies are standard JavaScript (.js) files, and used using Common JS’s require thing (but, Babel can make it better, you can just use the import key word to do the function, and babel will compile it to the code that support Common JS)
- All the source files of JavaScript all have the .jsx file extension to distinct from the js outputs (it has two meanings to use the .jsx file extension):
- Babel can compile the JSX code for React, so it is very natual
- You can make your JSX editor plugin (for example VIM plugin) to recognise this, and since JSX is a super set of ES6, so you’ll make your editor support your source files automaticly
- All the compiled result are using .js file extension, having the same file name
So, the pattern to compile all the source file is something like this:
%.js: %.jsx
$(SILENT) $(BABEL) -s inline -o $@ $<
The meaning of this pattern is like this:
- All the js output file can be compiled using the source code file of jsx, for example, if you want to make a js file named hello.js, make should find the source file named hello.jsx, and compile it using the operations below and make hello.js
$(SILENT)
is the make trick to have the debug output, let’s ignore it, suppose it is blank- Here is the operation
$(BABEL) -s inline -o $@ $<
this command means that I want babel to compile the source map into the output and$@
means the output file name$<
means the input file name
So, if you have this pattern in your Makefile, you’ll have the ability to make any JavaScript file by compiling the jsx file of same name, and store them in the same folder.
The Source Files And How To Build The Dist File
But how can you specify all the source files to let them to be compiled by make, you can use this way(so straight forward, but not wise)
SOURCE_FILES := hello.js world.js bye.js main.js
This is a little stupid, but straight forward.
Is there any better way to handle this? I’m using a macro to do this, it is something like this:
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
SRC_DIR := src
SRC_FILES := $(call rwildcard, $(SRC_DIR), *.jsx)
This settings will have all the jsx file in the src folder set as the variable SRC_FILES. Then you can use this variable to add the build task.
build.js: $(SRC_FILES)
$(SILENT) $(BROSERIFY) $(SRC_DIR)/app.js -o build.js
And even add the uglified version of the build.js
build.ugly.js: build.js
$(SILENT) $(UGLIFY) build.js -o build.ugly.js
How About Unit Testing?
For unit testing, we can just use Jasmine.
And using the variable and tasks like this:
SPEC_DIR := spec
SPEC_FILES := $(call rwildcard, $(SPEC_DIR), *.jsx)
test: $(SPEC_FILES)
$(SILENT) $(JASMINE)
The problem
So far, there is an problem using this way, is that the linker of browserify is quite qutie quite quite slow.
For my project, it’ll cost at least 6 seconds on a MacBookPro Retina 2015 to build the result , this is unbareable, so I decide to write a new linker(just the linker, which supports CMD requiring and add the glue code to make it works in a single file) in Go, and that’ll make it faster.
This will take a little time to finish, so, before that, I’ll just using browserify to do the job.
What Next?
So much for this blog post, I’ll write another blog post about the project setup, and after cleanup the code, I’ll create an project bootstrap on the github.com to let you understand the benifits of this method.