Sencha Inc. | HTML5 Apps

Blog

Make A JavaScript Formatter with V8 and jsbeautifier

October 21, 2010 | Ariya Hidayat

If you're a JavaScript developer, you should check out a handy online tool called jsbeautifier.org, written by Einar Lielmanis. By simply pasting your JavaScript code and clicking Beautify, the jsbeautifier tool automatically reformats and reindents your JavaScript code (or JSON snippets) based on spacing, packing, and other options that you can configure. A great feature of jsbeautifier is that the formatter has a JavaScript API (check it out here http://github.com/einars/js-beautify/), whose simple API call is js_beautify(js_source_text, options). This returns the formatted version of js_source_text based on the settings passed in in options. If you want to programmatically “beautify” local files, you can also run the jsbeautifier script from the command line. There is support for Rhino, a JavaScript interpreter written in Java, to invoke the beautifier with the content of any file. Or if you'd prefer to not use Rhino, I recently added support for Qt Script or V8. Qt Script is the JavaScript engine built into Qt, the multi-platform application framework from Nokia. V8 is the JavaScript engine that powers Google Chrome. In one of my previous blog posts, JavaScript Engines: How to Compile Them, I discussed how JavaScript engines can do more than just power a web browser. Today, I'll use the V8-based beautifier as an example of how to embed V8 in a command-line application.

Get the Code

Let’s start by checking out the code, whic is as easy as:
git clone git://github.com/einars/js-beautify.git
cd js-beautify/v8
What we want is in the v8/ subdirectory. Next, we need to compile V8, it should be under lib/. You can read JavaScript Engines: How to Compile Them for the details on this.
svn checkout -r 5610 http://v8.googlecode.com/svn/trunk lib
cd lib && scons mode=release && cd ..
Note: We're using V8 version 2.4.9 (revision 5610), earlier or later versions may have slightly different APIs. When you've successfully compiled V8, you will get a static library: lib/libv8.a. Next, compile the beautifer:
g++ -o jsbeautify jsbeautify.cpp -Ilib/include/ -lv8 -Llib
The resulting executable jsbeautify is the final tool. Just place jsbeautify in your PATH to use it. Now, you can set up your text editor to launch jsbeautify as an external application, and you have a handy auto-formatter! Try out the tool by passing a file name as the argument, and the beautified contents will be printed out:
./jsbeautify hello.js
You can use the --overwrite option if you want to overwrite the original file.

How It Works

You'll need a little background in C++ to understand the following code snippets (since V8 is written in C++). And to follow everything perfectly, you should also read the Getting Started Guide To V8 and the V8 Embedder's Guide. This is the annotated version of our tool:
 
   FILE* f = fopen(argv[1], "rb");
    if (!f) {
        printf("Error: unable to open file %s\n", argv[1]);
        return 0;
    }
    fseek(f, 0, SEEK_END);
    int len = ftell(f);
    rewind(f);
    char* buf = new char[len + 1];
    fread(buf, 1, len, f);
    fclose(f);
 
The first thing the program does is read the input file (the name is passed as an argument to the program) and load the contents to a buffer (allocated from the heap). Nothing fancy here, just basic I/O (written for simplicity). Obviously, the buffer lives in the C++ world, so we need to transfer it to the JavaScript world so it can be accessed by the beautifier function.
 
    HandleScope handle_scope;
    Handle<ObjectTemplate> global = ObjectTemplate::New();
    global->Set("code", String::New(buf, len));
    Handle<Context> context = Context::New(NULL, global);
 
As mentioned in the V8 Embedder's Guide, to use V8, we have to prepare a handle scope, a global object, and an execution context. To pass the buffer content from C++ to JavaScript, we set the property called 'code' in our global object to point to a new String object initialized with the buffer content. From this point, any JavaScript code running in the context can refer to and get the value of ‘code’.
 
    Context::Scope context_scope(context);
    Handle<Script> beautifyScript = Script::Compile(String::New(beautify_code));
    beautifyScript->Run();
 
Context needs to be entered before we can use it, this is easily achieved using the context scope. After that, we compile the script which actually implements the js_beautify function and then immediately evaluate it.
 
    Handle<Script> runnerScript = Script::Compile(String::New("js_beautify(code)"));
    Handle<Value> result = runnerScript->Run();
 
At this stage, the JavaScript context already has access to an object (which is actually a string) named ‘code’ and a function ‘js_beautify’. So now we just need to connect the dots: evaluate “js_beautify(code)” and get the result. We also need to transfer the result, which still lives in the JavaScript world, back to our application’s C++ world:
 
    if (!result.IsEmpty() && !result->IsUndefined()) {
        String::Utf8Value str(result);
        printf("%s\n", *str);
    }   
 
The final step is to print it out. And that’s all! This example shows how you can quickly use V8 to wrap and execute a JavaScript function and turn it into a real-world tool. I hope it serves as a good start for you to explore the features of V8 as an embedded scripting engine!

There are 13 responses. Add yours.

Albert

4 years ago

thanks. this is pretty cool stuff

Bill

4 years ago

This is probably me missing something stupid, but I get this error when trying to compile the beautifer (using a MacBook Pro):

ld: warning: in lib/libv8.a, file was built for unsupported file format which is not the architecture being linked (x86_64)

Ariya Hidayat

4 years ago

@Bill: Pass ‘arch=x64’, i.e. use ‘scons mode=release arch=x64’ to build V8 (it’s omitted in this blog post, but somehow mentioned in the js-beautify repo’s README.txt ).

Mango_lier

4 years ago

I built a plugin for PSPad based on Einar’s code. You can pull in a file in the editor and select format from the menu.

Download: http://www.pspad.com/files/pspad/rozsireni/181-js_beautify.zip

Enc

4 years ago

I would like to respectfully disagree with anyone who thinks that this article is in any way close to “cool”.
A _cool_ article or, in other words, a useful, informative and insightful article, would actually contain information that is more useful than a stub page on Wikipedia.

This article teaches ‘JavaScript’ developers who supposedly don’t know any better or are not that good with C++ that it’s not such a bad idea to create a command line tool in C++ that embeds a JavaScript interpreter written in C++ to run a JavaScript parser script written in JavaScript to format JavaScript code.

Well, I have news for you: that is not in any way _cool_ or useful. In fact, it’s redundant, unnecessary, a misuse of jsbeautifier and a waste of resources.

To see why, let’s take this article even further in ridiculousness. So far we’ve got a command line tool embedding V8 to run a JS script that parses JS code and outputs the result. Now, create a virtual machine to virtualize this tool and host the VM on Amazon, provide a REST API for your users to POST their code with curl and pipe the XML output to an XSLT processor which will use an XSL stylesheet to format the XML and transform it back to properly indented and colorized JavaScript.

If you still think that the above is _cool_ and not outrageous, then you have failed.

Obviously, one might argue that the goal was to show how to embed V8 and run some JavaScript code in it. In that case, I still question the usefulness of this article, since it could have been simply reduced to the following sentence: “RTFM @ http://code.google.com/apis/v8/embed.html”.

A more useful article for creating a command-line JavaScript “beautifier” using V8 would have been one that explains how to reuse functionality from V8 (mainly the parse tree I imagine it must construct) to print properly indented and highlighted JavaScript code, without actually wasting resources on interpreting any of it.

Overall, I say 100/100 for the good intentions and initiative, but a 50/100 for the content.

Mischa Kroon

4 years ago

Rhino (javascript engine used by java) seems like the easier option, allthough it probably has quite a bit of overhead.

Ariya Hidayat

4 years ago

@Enc. Thanks for the feedback! This is not the last blog post in this series and we’ll cover more advanced and useful stuff (and yes, I’ve done syntax tree-related stuff as well) as we go along.

Animal

4 years ago

@Ariya, loving your self-restraint!

Sergiu

4 years ago

Or you can use Aptana -  CTRL-SHIFT-F

Spike

3 years ago

At last! Someone who undrestands! Thanks for posting!

Ashton

3 years ago

thanks for the tool. I am just starting javascript and its good to have this tool at this early.

Comments are Gravatar enabled. Your email address will not be shown.

Commenting is not available in this channel entry.