Anshul Goyal I love to code and use new technologies.

Speed up JavaScript builds with spack bundler in Rust

3 min read 933

swc bundler

Because most JavaScript bundlers and transpilers are written in JavaScript itself, they tend to lack efficiency. However, if bundlers and transpilers were written in a faster, better compiled, and optimized language like Python or Rust, they could operate faster.

In this article, we will discuss how to use the spack bundler with swc-project in Rust to speed up JavaScript builds.

What is swc-project?

swc-project is a collection of bundlers and a transpilers written in Rust. swc-project can transpile Typescript, JSX, TSX, and other versions of JavaScript. Like Babel, SWC is a tool for reading source code; however, SWC works about 20 times faster than Babel.

swc can be configured using the .swcrc file; see different configurations here.

Transpiling through swc with spack

spack is a JS bundler with support for transpiling through swc. It is an alternative for webpack with support for dead code elimination and tree shaking.

spack is purely written in Rust and used by Deno; Deno recently added a new TypeScript bundling and transpiling option based on spack.

spack internally relies on swc_bundler crate for assets bundling.

const { config } = require("@swc/core/spack");
const path = require("path");
module.exports = config({
 // starting point
  entry: {
    web: path.join(__dirname, "src", "index.ts"),
  },
 // output file
  output: {
    path: path.join(__dirname, "dist"),
  },
  module: {},
});

Configuring bundlers and transpilers

spack is configured using .swcrc files for transpilers and spack.config.js files for bundlers.

To see how it works, create a directory named spack-demo. In the spack-demo folder, add the index.js file and multiple files for demo purposes. Add spack.config.js and .swcrc file to directory.

The file structure should look like this.

We made a custom demo for .
No really. Click here to check it out.

├── index.js
├── spack.config.js
└── .swcrc

Install required packages using npm i @swc/core @swc/cli @swc/helpers.

For bundling: spack.config.js

// importing config creator
const { config } = require("@swc/core/spack");

// import path module
const path = require("path");

// export config
module.exports = config({

// start file
  entry: {
    build: path.join(__dirname, "index.js"),
  },

// output file
  output: {
    path: path.join(__dirname),
  },
  module: {},
});

For transpiling: .swcrc

{
    "jsc": {
// transpile to es2015
        "target": "es2015",
        "parser": {
// file use ecmascript
            "syntax": "ecmascript",
        }
    }
}

Using spack to bundle JavaScript

The boilerplate code we added previously can be used for the basic bundling and transpiling required by most code bases like React.js and Node.js.

For a demo, create a directory named mod with index.js and import index.js in the root. The file structure should look like this.

├── index.js
├── mod
│   └── index.js
├── spack.config.js
└── .swcrc

Note that mod/index.js and index.js contain dummy code for demonstrating the spack bundling features:

mod/index.js

export const sum=(a,b)=>a+b;

index.js

import {sum as add} from "./mod"
console.log(add(1,2));

build.js (This file is generated by spack)

var sum = function(a, b) {
    return a + b;
};
console.log(sum(1,2));

Transpiling to older JavaScript

spack uses swc for transpiling to multiple version of JavaScript according to the target key in .swcrc file. swc supports ES5, ES2015, ES2016, ES2018, ES2019 and ES2020.

In our previous demo, we wrote code supported by the newer ES2015 version of Javascript, but with spack, we can transpile our code to older versions of JavaScript that are supported by older browsers. Almost all browsers support the ES5 version of JavaScript.

To see how it works, change target to es5 and add some newer code to mod/index.js.

mod/index.js

export const sum=(a,b)=>({...a,...b})

.swcrc

{
    "jsc": {
// transpile to es5
        "target": "es5",
        "parser": {
// file use ecmascript
            "syntax": "ecmascript",
        }
    }
}

Your build.js output file will now look like this:

function _defineProperty(obj, key, value) {
    if (key in obj) {
        Object.defineProperty(obj, key, {
            value: value,
            enumerable: true,
            configurable: true,
            writable: true
        });
    } else {
        obj[key] = value;
    }
    return obj;
}
function _objectSpread(target) {
    for(var i = 1; i < arguments.length; i++){
        var source = arguments[i] != null ? arguments[i] : {
        };
        var ownKeys = Object.keys(source);
        if (typeof Object.getOwnPropertySymbols === "function") {
            ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
                return Object.getOwnPropertyDescriptor(source, sym).enumerable;
            }));
        }
        ownKeys.forEach(function(key) {
            _defineProperty(target, key, source[key]);
        });
    }
    return target;
}
var sum = function(a, b) {
    return _objectSpread({
    }, a, b);
};
console.log(sum({
}, {
}));

When you take a closer look to at the build.js output file, you’ll see that it now contains some extra code to support the newer JS features (like spread operator). All of the additional code is injected by spack so that our code can run perfectly on browsers old and new, including those that only support the ES5 version of JavaScript.

Bundling multiple JavaScript entries

When dealing with multiple bundles, the build can be optimized by caching common files or modules and avoiding duplicate work. spack supports multiple bundles simultaneously with caching common files across bundles.

spack.config.js

const { config } = require("@swc/core/spack");
const path = require("path");
module.exports = config({
  entry: {
    web: path.join(__dirname, "src", "index.js"),
    node: path.join(__dirname, "src", "node.js"),
  },
  output: {
    path: path.join(__dirname, "dist"),
  },
  module: {},
});

Bundling TypeScript

spack is used by Deno for transpiling and bundling TypeScript. It has full support for TypeScript transpilation and bundling using swc. swc compiled passes all the tests on TypeScript repo. TypeScript can be supported by changing the syntax key to TypeScript.

{
    "jsc": {
// transpile to es5
        "target": "es5",
        "parser": {
// file use ecmascript
            "syntax": "typescript",
        }
    }
}

Conclusion

spack is in its early stages, but until it is production-ready, it can still be used in Rust swc-project for bundling in the development environment. spack can also be used as a base library for creating a more robust bundler with multiple features, just as Deno has used for bundling TypeScript.

LogRocket: Full visibility into production Rust apps

Debugging Rust applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking performance of your Rust apps, automatically surfacing errors, and tracking slow network requests and load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your Rust app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.

Modernize how you debug your Rust apps — .

Anshul Goyal I love to code and use new technologies.

One Reply to “Speed up JavaScript builds with spack bundler in Rust”

  1. ❝a faster, better compiled, and optimized language like Python or Rust❞
    Are you kidding? OK for Rust, but Python is anything but.

Leave a Reply