Creating JavaScript modules with Babel and ES7

Creating JavaScript modules with Babel and ES7

Last year, a new version of JavaScript was released with a lot of new goodies. One of them was that the syntax for importing and exporting modules was codified as the "only way" to handle JavaScript modules. Well, it's finally here. Another good thing is that it's organized in a way that you can statically analyze the entire module dependency tree. Pretty cool.

Let's take a look at the syntax:

  1. import v from "mod" ;
  2. import * as obj from "mod" ;
  3. import {x} from "mod" ;
  4. import {x as v} from "mod" ;
  5. import   "mod" ;
  6.  
  7. export   var v;
  8. export   default   function f(){};
  9. export   default   function (){};
  10. export   default 42;
  11. export {x};
  12. export {x as v};
  13. export {x} from "mod" ;
  14. export {x as v} from "mod" ;
  15. export * from "mod" ;

So, basically you can import the main value of a module ("default"), or a specific property from explicit exports, or a combination of both, or anything else. Correspondingly, you can export a value for the default module, or an object with multiple properties. You can also export properties one by one. ***Please refer to the style guide for style ;)

ES7 also adds some small additions to this kind of syntax.

  1. export * as ns from "mod" ;
  2. export v from "mod" ;

Nothing special, but when can we use them? We'll have to wait and see. Just like many ES6 syntax features, if you don't plan to support them now, you can use a tool called Babel to transpile them back to ES5. Once you are ready to support them, you can stop Babel from transpiling.

Let's take a look at how this works. We'll implement this in Node.js and NPM. Try executing this file;

src/letter_keys.js

  1. // you would have a constant for each key  
  2. // (I would normally uppercase all constants)  
  3. const a = 119;
  4. const d = 100;
  5. const s = 115;
  6. const w = 119;
  7.  
  8. // you would export all keys here  
  9. // note: you can't say `w: 119` here. It just isn't valid.  
  10. // This destructures to `w: w, a: a, ...`  
  11. export {
  12. w,
  13. a,
  14. d,
  15. s,
  16. }

src/arrow_keys.js

  1. const UP = 38;
  2. const RIGHT = 39;
  3. const DOWN = 40;
  4. const LEFT = 37;
  5.  
  6. export {
  7. UP,
  8. RIGHT,
  9. DOWN,
  10. LEFT,
  11. }

src/move.js

  1. export {a, w, s, d} from './letter_keys' ;
  2. export * as ARROWS from './arrow_keys' ;

The assumption is that the main index.js file is used for the export of the internal module contents, and it assumes that these keys are exported from other files. This example is a bit convoluted, but that's okay.

src/index.js

  1. import * as keys from './move' ;
  2. console.log(keys);

This can be used as part of a project that depends on this module. It should print the awsd key and the arrow object. Let's start with npm. Create the repo dir and initialize it:

  1. ~$ mkdir foo
  2. ~$ cd foo
  3. ~/foo$ mkdir src
  4. # put src files above in ~/foo/src  
  5. ~/foo$ npm init -yes
  6. ~/foo$ npm install babel-cli babel-preset-es2015 babel-preset-stage-1 -D

This will take a while. As you might have guessed, babel-cli supports running Babel from the command line (6), and the babel-preset-stage-1 package provides the relevant ES7 module transpilation tools (at the time of writing). The -yes flag will cause npm to create a default package.json without asking the user. The -D flag is short for --save-dev, and will add the package to the devDependency entry in package.json. Now, add the preset parameters to the default babel configuration file:

.babelrc

  1. {
  2.    "presets" : [ "es2015" , "stage-1" ]
  3. }

If this works, great! hello, future! However, at the time of writing, none of these examples will work in ES6, let alone Node.js. After these transpilation steps, they can be executed anyway.

You should now have an almost empty package.json file that contains the three dev dependencies we added. Let's add a script to this package.json file to enable transpilation:

  1. ...
  2.    "scripts" : {
  3.      "test" : "echo " Error: no test specified " && exit 1" ,
  4.      "translate" : "node_modules/babel-cli/bin/babel-node.js src/index.js"  
  5. },
  6. ...

(Just add the "translate" line and the comma after the "test" line).

This transpilation script is a compilation step. At the end of the article, you can find the final package.json file used in this article (official version). Now, all that’s left is to call npm to run the script to transpile and run our code.

  1. ~/foo$ npm run translate --silent
  2.  
  3. { A: [Getter],
  4. W: [Getter],
  5. S: [Getter],
  6. D: [Getter],
  7. ARROWS: { UP: 38, RIGHT: 39, DOWN: 40, LEFT: 37 } }

Go for it! Now, as a bonus, we can use Jscrambler to "obfuscate" the code a bit. We can pass Babel transpiled code, so why not?

Our (final) package.json file looks like this:

package.json

  1. {
  2.    "name" : "foo" ,
  3.    "version" : "1.0.0" ,
  4.    "description" : "" ,
  5.    "main" : "index.js" ,
  6.    "scripts" : {
  7.      "test" : "echo " Error: no test specified " && exit 1" ,
  8.      "translate" : "node_modules/babel-cli/bin/babel-node.js src/index.js"  
  9. },
  10.    "keywords" : [],
  11.    "author" : "Your Name <[email protected]> (http://localhost/)" ,
  12.    "license" : "ISC" ,
  13.    "devDependencies" : {
  14.      "babel-cli" : "6.6.5" ,
  15.      "babel-preset-es2015" : "6.6.0" ,
  16.      "babel-preset-stage-1" : "6.5.0" ,
  17.      "jscrambler" : "0.7.5"  
  18. }
  19. }

Complete the configuration as usual (if you are using Node.js, you will need a pro account). Here are the files I used

.jscramblerrc

  1. {
  2.    "keys" : {
  3.      "accessKey" : "See https://jscrambler.com/en/account/api_access" ,
  4.      "secretKey" : "See https://jscrambler.com/en/account/api_access"  
  5. },
  6.    "params" : {
  7.      "constant_folding" : "%DEFAULT%" ,
  8.      "dead_code" : "%DEFAULT%" ,
  9.      "dead_code_elimination" : "%DEFAULT%" ,
  10.      "dictionary_compression" : "%DEFAULT%" ,
  11.      "dot_notation_elimination" : "%DEFAULT%" ,
  12.      "function_outlining" : "%DEFAULT%" ,
  13.      "function-reorder" : "%DEFAULT%" ,
  14.      "literal_duplicates" : "%DEFAULT%" ,
  15.      "literal_hooking" : "2;8" ,
  16.      "member_enumeration" : "%DEFAULT%" ,
  17.      "mode" : "nodejs" ,
  18.      "rename_local" : "%DEFAULT%" ,
  19.      "string_splitting" : "0.3" ,
  20.      "whitespace" : "%DEFAULT%"  
  21. }
  22. }

Let's summarize it with a script. This script will translate the source files with Babel, output to the /build folder, then use Jscrambler to obfuscate the code, and then put the result into the /dist folder. The content in /dist can be run normally without using any ES7 features.

run.sh

  1. #!/bin/sh  
  2.  
  3. echo "Babelifying src/*.js"  
  4. node_modules/babel-cli/bin/babel.js -d build src/*.js
  5. echo "Scrambling build/*.js"  
  6. node_modules/jscrambler/bin/jscrambler -o dist build/src/**
  7. echo "Clean up artifacts"  
  8. mv dist/build/src/* dist/
  9. rmdir dist/build/src
  10. rmdir dist/build
  11. echo "Done! See dist/scrambled.js"  
  12. echo "Running:"  
  13. node dist/index.js

Make it ready:

  1. chmod +x run.sh

Next, run:

  1. ~/foo$ ./run.sh
  2. Babelizing src/*.js
  3. src/arrow_keys.js -> build/src/arrow_keys.js
  4. src/index.js -> build/src/index.js
  5. src/letter_keys.js -> build/src/letter_keys.js
  6. src/move.js -> build/src/move.js
  7. Scrambling build/*.js
  8. Clean up artifacts
  9. Done! See dist/ for your scrambled files
  10. Running:
  11. { a: [Getter],
  12. w: [Getter],
  13. s: [Getter],
  14. d: [Getter],
  15. ARROWS: { UP: 38, RIGHT: 39, DOWN: 40, LEFT: 37 } }

You can go to the /dist folder to see the result. You will see that the result is far from the original file, this is because we protected it through Jscrambler, but the result is still runnable.

This concludes the tutorial. Have fun using ES7!

OneAPM helps you easily identify the performance bottleneck of Node.js applications, and analyzes layer by layer through powerful Trace records until the line-level problem code is identified. It displays the system response speed from the user's perspective and counts user usage by region and browser.

<<:  FastQuery: a framework for fast data operations

>>:  What do you need to know to ensure mobile app quality?

Recommend

From super TV to super car, the future life guided by LeTV

After the underlying LeTV business develops, hard...

5 analyses of B station’s marketing!

In 2020, Bilibili has successfully "broken o...

Are you laughing at Erha? He has been to the Olympics!

When people mention Huskies, they always associat...

From 0 to 1, interpreting Android ASO optimization!

Hello everyone, I am an ASO operations specialist...

GSMA: 50% of Chinese consumers hope to buy 5G mobile phones as soon as possible

Smartphones will remain the dominant consumer dev...

Shengtao E-commerce: 2022 anchor advanced training online column worth 980 yuan

Shengtao E-commerce: 2022 anchor advanced trainin...

Export suspended! What exactly is natural sand?

Recently, according to the official website of th...

Frameworks and tools that hybrid app developers must not miss

I recently started to select technology for the m...

Wuxi Guoda Machinery Equipment Co., Ltd. SEO optimization training case

Wuxi Guoda Machinery Equipment Co., Ltd. is a for...

Best Practices for Customizing Android BaseAdapter

Although many new projects are using RecyclerView...