Skip to content

Package your Node.js application into a single executable file, but only 10MB.πŸ”₯

License

Notifications You must be signed in to change notification settings

Ray-D-Song/lexe

Folders and files

NameName
Last commit message
Last commit date

Latest commit

48f6ba2 Β· Apr 16, 2025
Sep 18, 2024
Apr 14, 2025
Mar 24, 2025
Apr 4, 2025
Feb 24, 2025
Apr 13, 2025
Apr 13, 2025
Apr 4, 2025
Apr 4, 2025
Apr 13, 2025
Mar 6, 2025
Apr 4, 2025
Mar 29, 2025
May 2, 2024
Apr 13, 2025
Oct 28, 2024
Sep 18, 2024
Mar 8, 2024
Mar 29, 2025
Feb 9, 2025
Mar 8, 2024
Apr 13, 2025
Apr 4, 2025
Mar 8, 2024
Feb 12, 2024
Feb 5, 2025
Feb 12, 2024
Apr 16, 2025
Apr 16, 2025
Oct 27, 2023
Mar 21, 2025
Oct 15, 2024
Jan 7, 2024
Apr 11, 2025
Apr 13, 2025
Apr 30, 2024
Apr 13, 2025
Apr 5, 2024
Dec 9, 2024
Aug 11, 2024
Mar 8, 2024
Apr 11, 2025

Repository files navigation

Lexe

δΈ­ζ–‡ζ–‡ζ‘£

Lexe is a fork of AWS's lightweight JavaScript runtime "LLRT".
You can use it to package your Node.js applications into a single executable file, but the size is only 8~10MB.

Size comparison

npx lexe build -i=index.js

npx lexe build -i=index.js -o=binary -d=dist -p=linux-x64,windows-x64

Options:

  • -i: input file(required)
  • -o: output file(optional, default: <input file name>-<platform>)
  • -d: output directory(optional, default: ./dist)
  • -p: target platform, use "," to separate multiple platforms options: linux-x64,linux-arm64,darwin-x64,darwin-arm64,windows-x64 (optional, default: current platform)

Important

Lexe(or LLRT) currently does not support the complete http and https modules, but implements fetch for requests and net for low-level network services.
In the LLRT team's ROADMAP, http and https modules are a 2025 goal
So I currently do not recommend using Lexe to build HTTP services, but to create CLI tools

Warning

Lexe is not a drop-in replacement for Node.js. It only supports a subset of Node.js APIs.
You can read more about LLRT in LLRT README
Since Lexe is a fork of LLRT, the following document is basically a copy of LLRT

Compatibility matrix

Modules Node.js LLRT ⚠️
assert βœ”οΈŽ βœ”οΈŽοΈ
buffer βœ”οΈŽ βœ”οΈŽοΈ
child_process βœ”οΈŽ βœ”οΈŽβ±
console βœ”οΈŽ βœ”οΈŽ
crypto βœ”οΈŽ βœ”οΈŽ
dns βœ”οΈŽ βœ”οΈŽ
events βœ”οΈŽ βœ”οΈŽ
fs/promises βœ”οΈŽ βœ”οΈŽ
fs βœ”οΈŽ ✘⏱
http βœ”οΈŽ ✘⏱**
https βœ”οΈŽ ✘⏱**
net:sockets βœ”οΈŽ βœ”οΈŽβ±
net:server βœ”οΈŽ βœ”οΈŽ
os βœ”οΈŽ βœ”οΈŽ
path βœ”οΈŽ βœ”οΈŽ
perf_hooks βœ”οΈŽ βœ”οΈŽ
process βœ”οΈŽ βœ”οΈŽ
streams βœ”οΈŽ βœ”οΈŽ*
string_decoder βœ”οΈŽ βœ”οΈŽ
timers βœ”οΈŽ βœ”οΈŽ
tty βœ”οΈŽ βœ”οΈŽ
url βœ”οΈŽ βœ”οΈŽ
util βœ”οΈŽ βœ”οΈŽ
tls βœ”οΈŽ ✘⏱
zlib βœ”οΈŽ βœ”οΈŽ
Other modules βœ”οΈŽ ✘
Features Node.js LLRT ⚠️
async/await βœ”οΈŽ βœ”οΈŽ
encoding βœ”οΈŽ βœ”οΈŽ
fetch βœ”οΈŽ βœ”οΈŽ
ESM βœ”οΈŽ βœ”οΈŽ
CJS βœ”οΈŽ βœ”οΈŽ

⚠️ = partially supported in LLRT
⏱ = planned partial support
* = Not native
** = Use fetch instead

Using node_modules (dependencies) with llrt

Since llrt is meant for performance critical application it's not recommended to deploy node_modules without bundling, minification and tree-shaking.

llrt can work with any bundler of your choice. Below are some configurations for popular bundlers:

Warning

LLRT implements native modules that are largely compatible with the following external packages. By implementing the following conversions in the bundler's alias function, your application may be faster, but we recommend that you test thoroughly as they are not fully compatible.

Node.js LLRT
fast-xml-parser llrt:xml
uuid llrt:uuid

ESBuild

esbuild index.js --platform=browser --target=es2023 --format=esm --bundle --minify --external:@aws-sdk --external:@smithy

Rollup

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import terser from "@rollup/plugin-terser";

export default {
  input: "index.js",
  output: {
    file: "dist/bundle.js",
    format: "esm",
    sourcemap: true,
    target: "es2023",
  },
  plugins: [resolve(), commonjs(), terser()],
  external: ["@aws-sdk", "@smithy"],
};

Webpack

import TerserPlugin from "terser-webpack-plugin";
import nodeExternals from "webpack-node-externals";

export default {
  entry: "./index.js",
  output: {
    path: "dist",
    filename: "bundle.js",
    libraryTarget: "module",
  },
  target: "web",
  mode: "production",
  resolve: {
    extensions: [".js"],
  },
  externals: [nodeExternals(), "@aws-sdk", "@smithy"],
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          ecma: 2023,
        },
      }),
    ],
  },
};

Running TypeScript with LLRT

Same principle as dependencies applies when using TypeScript. TypeScript must be bundled and transpiled into ES2023 JavaScript.

Note

LLRT will not support running TypeScript without transpilation. This is by design for performance reasons. Transpiling requires CPU and memory that adds latency and cost during execution. This can be avoided if done ahead of time during deployment.

Rationale

What justifies the introduction of another JavaScript runtime in light of existing options such as Node.js, Bun & Deno?

Node.js, Bun, and Deno represent highly proficient JavaScript runtimes. However, they are designed with general-purpose applications in mind. These runtimes were not specifically tailored for the demands of a Serverless environment, characterized by short-lived runtime instances. They each depend on a (Just-In-Time compiler (JIT) for dynamic code compilation and optimization during execution. While JIT compilation offers substantial long-term performance advantages, it carries a computational and memory overhead.

In contrast, LLRT distinguishes itself by not incorporating a JIT compiler, a strategic decision that yields two significant advantages:

A) JIT compilation is a notably sophisticated technological component, introducing increased system complexity and contributing substantially to the runtime's overall size.

B) Without the JIT overhead, LLRT conserves both CPU and memory resources that can be more efficiently allocated to code execution tasks, thereby reducing application startup times.

Limitations

There are many cases where LLRT shows notable performance drawbacks compared with JIT-powered runtimes, such as large data processing, Monte Carlo simulations or performing tasks with hundreds of thousands or millions of iterations. LLRT is most effective when applied to smaller Serverless functions dedicated to tasks such as data transformation, real time processing, AWS service integrations, authorization, validation etc. It is designed to complement existing components rather than serve as a comprehensive replacement for everything. Notably, given its supported APIs are based on Node.js specification, transitioning back to alternative solutions requires minimal code adjustments.

Environment Variables

LLRT_EXTRA_CA_CERTS=file

Load extra certificate authorities from a PEM encoded file

LLRT_GC_THRESHOLD_MB=value

Set a memory threshold in MB for garbage collection. Default threshold is 20MB

LLRT_HTTP_VERSION=value

Extends the HTTP request version. By default, only HTTP/1.1 is enabled. Specifying '2' will enable HTTP/1.1 and HTTP/2.

LLRT_LOG=[target][=][level][,...]

Filter the log output by target module, level, or both (using =). Log levels are case-insensitive and will also enable any higher priority logs.

Log levels in descending priority order:

  • Error
  • Warn | Warning
  • Info
  • Debug
  • Trace

Example filters:

  • warn will enable all warning and error logs
  • llrt_core::vm=trace will enable all logs in the llrt_core::vm module
  • warn,llrt_core::vm=trace will enable all logs in the llrt_core::vm module and all warning and error logs in other modules

LLRT_NET_ALLOW="host[ ...]"

Space-delimited list of hosts or socket paths which should be allowed for network connections. Network connections will be denied for any host or socket path missing from this list. Set an empty list to deny all connections

LLRT_NET_DENY="host[ ...]"

Space-delimited list of hosts or socket paths which should be denied for network connections

LLRT_NET_POOL_IDLE_TIMEOUT=value

Set a timeout in seconds for idle sockets being kept-alive. Default timeout is 15 seconds

LLRT_PLATFORM=value

Used to explicitly specify a preferred platform for the Node.js package resolver. The default is browser. If node is specified, "node" takes precedence in the search path. If a value other than browser or node is specified, it will behave as if "browser" was specified.

LLRT_TLS_VERSION=value

Set the TLS version to be used for network connections. By default only TLS 1.2 is enabled. TLS 1.3 can also be enabled by setting this variable to 1.3

Benchmark Methodology

Although Init Duration reported by Lambda is commonly used to understand cold start impact on overall request latency, this metric does not include the time needed to copy code into the Lambda sandbox.

The technical definition of Init Duration (source):

For the first request served, the amount of time it took the runtime to load the function and run code outside of the handler method.

Measuring round-trip request duration provides a more complete picture of user facing cold-start latency.

Lambda invocation results (Ξ»-labeled row) report the sum total of Init Duration + Function Duration.

License

This library is licensed under the Apache-2.0 License. See the LICENSE file.

About

Package your Node.js application into a single executable file, but only 10MB.πŸ”₯

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published