Nim: "Hello World" JavaScript bundle size nearly 4kb

Created on 16 Jul 2020  路  5Comments  路  Source: nim-lang/Nim

I'm new to nim and was quite excited by the nim=>JS compilation but was a bit surprised at a ~4kb JS file on compilation of a hello world. Is this expected? Will this scale linearly or is this just the "bootstrapping" of the nim=>JS bindings?

Example

echo "Hello World!"

Current Output

A 3.9K JS file (see below)

Expected Output

Something as close as possible to:

console.log("Hello World!")

Possible Solution

I don't know per-say, perhaps passing the JS through some sort of optimizer/minimizer?

Additional Information

I assume this is an expected property of compiling from one language (nim) to another (js), but wondering will this translate to larger applications when transpiling to JS?

/* Generated by the Nim Compiler v1.2.4 */
var framePtr = null;
var excHandler = 0;
var lastJSError = null;
if (typeof Int8Array === 'undefined') Int8Array = Array;
if (typeof Int16Array === 'undefined') Int16Array = Array;
if (typeof Int32Array === 'undefined') Int32Array = Array;
if (typeof Uint8Array === 'undefined') Uint8Array = Array;
if (typeof Uint16Array === 'undefined') Uint16Array = Array;
if (typeof Uint32Array === 'undefined') Uint32Array = Array;
if (typeof Float32Array === 'undefined') Float32Array = Array;
if (typeof Float64Array === 'undefined') Float64Array = Array;
function toJSStr(s_225096) {
                    var Tmp5;
            var Tmp7;

  var result_225097 = null;

    var res_225170 = new_seq_225128((s_225096 != null ? s_225096.length : 0));
    var i_225172 = 0;
    var j_225174 = 0;
    L1: do {
        L2: while (true) {
        if (!(i_225172 < (s_225096 != null ? s_225096.length : 0))) break L2;
          var c_225175 = s_225096[i_225172];
          if ((c_225175 < 128)) {
          res_225170[j_225174] = String.fromCharCode(c_225175);
          i_225172 += 1;
          }
          else {
            var helper_225198 = new_seq_225128(0);
            L3: do {
                L4: while (true) {
                if (!true) break L4;
                  var code_225199 = c_225175.toString(16);
                  if (((code_225199 != null ? code_225199.length : 0) == 1)) {
                  if (helper_225198 != null) { helper_225198.push("%0"); } else { helper_225198 = ["%0"]; };
                  }
                  else {
                  if (helper_225198 != null) { helper_225198.push("%"); } else { helper_225198 = ["%"]; };
                  }

                  if (helper_225198 != null) { helper_225198.push(code_225199); } else { helper_225198 = [code_225199]; };
                  i_225172 += 1;
                    if (((s_225096 != null ? s_225096.length : 0) <= i_225172)) Tmp5 = true; else {                      Tmp5 = (s_225096[i_225172] < 128);                    }                  if (Tmp5) {
                  break L3;
                  }

                  c_225175 = s_225096[i_225172];
                }
            } while(false);
++excHandler;
            Tmp7 = framePtr;
            try {
            res_225170[j_225174] = decodeURIComponent(helper_225198.join(""));
--excHandler;
} catch (EXC) {
 var prevJSError = lastJSError;
 lastJSError = EXC;
 --excHandler;
            framePtr = Tmp7;
            res_225170[j_225174] = helper_225198.join("");
            lastJSError = prevJSError;
            } finally {
            framePtr = Tmp7;
            }
          }

          j_225174 += 1;
        }
    } while(false);
    if (res_225170 === null) res_225170 = [];
               if (res_225170.length < j_225174) { for (var i=res_225170.length;i<j_225174;++i) res_225170.push(null); }
               else { res_225170.length = j_225174; };
    result_225097 = res_225170.join("");

  return result_225097;

}
function rawEcho() {
          var buf = "";
      for (var i = 0; i < arguments.length; ++i) {
        buf += toJSStr(arguments[i]);
      }
      console.log(buf);



}
function makeNimstrLit(c_225062) {
      var ln = c_225062.length;
  var result = new Array(ln);
  for (var i = 0; i < ln; ++i) {
    result[i] = c_225062.charCodeAt(i);
  }
  return result;



}
process.exitCode = 0;
var global_raise_hook_142018 = [null];
var local_raise_hook_142023 = [null];
var out_of_mem_hook_142026 = [null];
var unhandled_exception_hook_142031 = [null];
function new_seq_225128(len_225131) {
  var result_225133 = null;

  var F={procname:"newSeq.newSeq",prev:framePtr,filename:"system.nim",line:0};
  framePtr = F;
    F.line = 643;
    result_225133 = new Array(len_225131); for (var i=0;i<len_225131;++i) {result_225133[i]=null;}  framePtr = F.prev;

  return result_225133;

}
rawEcho(makeNimstrLit("Hello, World!"));

Most helpful comment

This result is expected. The Nim compiler don't "transpile" (as in generating idiomatic code) to JS, but rather "compile" and preserve Nim semantics. This will result in boilerplate support code being injected to maintain compatibility with Nim semantics.

These support code are only injected once and will be reused if possible, so the size of the program won't grow crazily.

All 5 comments

import jsconsole
console.log "hello world"

:arrow_down: :arrow_down: :arrow_down:

$ nim js -d:danger example.nim

:arrow_down: :arrow_down: :arrow_down:

/* Generated by the Nim Compiler v1.2.4 */
var framePtr = null;
var excHandler = 0;
var lastJSError = null;
if (typeof Int8Array === 'undefined') Int8Array = Array;
if (typeof Int16Array === 'undefined') Int16Array = Array;
if (typeof Int32Array === 'undefined') Int32Array = Array;
if (typeof Uint8Array === 'undefined') Uint8Array = Array;
if (typeof Uint16Array === 'undefined') Uint16Array = Array;
if (typeof Uint32Array === 'undefined') Uint32Array = Array;
if (typeof Float32Array === 'undefined') Float32Array = Array;
if (typeof Float64Array === 'undefined') Float64Array = Array;
var nim_program_result = 0;
var global_raise_hook_142018 = [null];
var local_raise_hook_142023 = [null];
var out_of_mem_hook_142026 = [null];
var unhandled_exception_hook_142031 = [null];

console.log("hello world");

This result is expected. The Nim compiler don't "transpile" (as in generating idiomatic code) to JS, but rather "compile" and preserve Nim semantics. This will result in boilerplate support code being injected to maintain compatibility with Nim semantics.

These support code are only injected once and will be reused if possible, so the size of the program won't grow crazily.

Thanks for the clarity, that is in line with what I was assuming. I guess people chain the nim compile to something like Uglify.js?

Thanks 馃嵑

@juancarlospaco is this just a trick for console.log or is what you are showing helpful for all JS code?

It is actually usable for Frontend yes.

Having said that, you can always create Frontend-oriented Nim packages and publish them to Nimble!.
:)

Was this page helpful?
0 / 5 - 0 ratings