Skip to content

ptrdom/scalajs-esbuild

Repository files navigation

scalajs-esbuild

scalajs-esbuild is a module bundler for Scala.js projects that use npm packages: it bundles the .js files emitted by the Scala.js compiler with their npm dependencies into a single .js file using esbuild.

sbt-scalajs-esbuild Scala version support

Getting started

Plugin should feel quite familiar to the users of well known scalajs-bundler, with the main difference being that there is no special handling of npmDependencies - they must be provided through package.json placed within esbuild directory in project's base.

Implementation intentionally exposes only a few settings for configuration - provided scripts should be enough to get started with typical projects, but any advanced configuration should be provided by modifying said scripts through their sbt settings - esbuildBundleScript and esbuildServeScript.

All sbt tasks that depend on Scala.js stages can be scoped both implicitly and explicitly, for example esbuildBundle will use scalaJSStage or the stage can be provided within the command - fastLinkJS/esbuildBundle/fullLinkJS/esbuildBundle.

Comparing to scalajs-vite, the main difference comes from the fact that Vite is not a bundler, it is a build tool - it uses esbuild in dev server implementation and Rollup for production bundles. Rollup is a significantly slower bundler than esbuild while also having no concept of development bundles. Development bundles must both behave the same as production bundles and be fast to produce - the latter partially because they are missing optimisations important only in production. Because in Scala.js projects these development bundles are actually very useful in certain workflows, particularly in the implementation of tests, scalajs-esbuild brings esbuild's performance into every aspect of Scala.js development workflow.

Base plugin

Base plugin is designed for Node or browser libraries, CLIs or standalone scripts - for web apps please use web plugin.

Basic setup

  1. Setup project layout, following is a minimal example:

    src
      main
       scala
         example
           Main.scala # Scala.js entry point
    esbuild
      package.json # devDependencies must provide esbuild package
    
  2. Add plugin to sbt project:

    addSbtPlugin("me.ptrdom" % "sbt-scalajs-esbuild" % pluginVersion)
  3. Enable plugin in build.sbt:

    enablePlugins(ScalaJSEsbuildPlugin)
    
  4. Specify that Scala.js project is an application with an entry point:

    scalaJSUseMainModuleInitializer := true
    
  5. Use sbt tasks to compile Scala.js code and run esbuild:

    • esbuildBundle
      • Bundles are produced in /target/${scalaVersion}/esbuild/main/out directory.
    • run/test
      • esbuildBundle output will be fed to jsEnvInput.

See examples for project templates.

Web plugin

Web plugin is designed for web apps. Because esbuild does not have a full-fledged build-in dev server and HTML entry point transformations like Vite, this plugin attempts to provide good enough stand-ins to enable typical workflows.

Basic setup

  1. Setup project layout, following is a minimal example:

    src
      main
       scala
         example
           Main.scala # Scala.js entry point
    esbuild
      index.html # esbuild HTML entry point
      package.json # devDependencies must provide esbuild and parse5 packages
    
  2. Add plugin to sbt project:

    addSbtPlugin("me.ptrdom" % "sbt-scalajs-esbuild-web" % pluginVersion)
  3. Enable plugin in build.sbt:

    enablePlugins(ScalaJSEsbuildWebPlugin)
  4. Specify that Scala.js project is an application with an entry point:

    scalaJSUseMainModuleInitializer := true

    Such configuration would allow main.js bundle to be used in esbuild HTML entry point:

    <script src="/main.js"></script>

    Entry points can be configured with esbuildBundleHtmlEntryPoints setting.

  5. Use sbt tasks to compile Scala.js code and run esbuild:

    • esbuildBundle
      • In addition to base plugin behavior, web implementation also does HTML entry point transformation - injection Scala.js entry points and CSS files into HTML.
    • ~esbuildServe
      • Starts dev server on port 3000.
        • Port can be configured with esbuildServe / serverPort setting.
      • Watches for updates to Scala.js code and any resources in esbuild directory.
      • CSS can be hot reloaded, changes to Scala.js code will cause a page reload.
    • esbuildServeStart;~esbuildStage;esbuildServeStop
      • Starting of dev server and watching of sources can be done in separate commands too.

See examples for project templates.

Electron plugin

Electron plugin is designed to enable Electron app development with Scala.js. Besides bundling for tests and production builds, the plugin also provides a dev server implementation.

Basic setup

  1. Setup project layout, following is a minimal example:

    src
      main
       scala
         example
           Main.scala # main process
           Preload.scala # preload script
           Renderer.scala # renderer process
    esbuild
      index.html # esbuild HTML entry point
      package.json # devDependencies must provide esbuild, parse5 and electron packages
    
  2. Add plugin to sbt project:

    addSbtPlugin("me.ptrdom" % "sbt-scalajs-esbuild-electron" % pluginVersion)
  3. Enable plugin in build.sbt:

    enablePlugins(ScalaJSEsbuildElectronPlugin)
  4. Configure Scala.js entry points and Electron process model components:

    scalaJSModuleInitializers := Seq(
      ModuleInitializer
        .mainMethodWithArgs("example.Main", "main")
        .withModuleID("main"),
      ModuleInitializer
        .mainMethodWithArgs("example.Preload", "main")
        .withModuleID("preload"),
      ModuleInitializer
        .mainMethodWithArgs("example.Renderer", "main")
        .withModuleID("renderer")
    )
    
    Compile / esbuildElectronProcessConfiguration := new EsbuildElectronProcessConfiguration(
      "main",
      Set("preload"),
      Set("renderer")
    )   

    Such configuration would allow renderer.js bundle to be used in esbuild HTML entry point:

    <script src="./renderer.js"></script>

    Entry points can be configured with esbuildBundleHtmlEntryPoints setting.

  5. Use web plugin sbt tasks to compile Scala.js code and run esbuild.

See examples for project templates.

Integrating with sbt-web

  1. Add plugin to sbt project:

    addSbtPlugin("me.ptrdom" % "sbt-web-scalajs-esbuild" % pluginVersion)
  2. Enable plugin in build.sbt:

    lazy val server = project
      .settings(
        scalaJSProjects := Seq(client),
        pipelineStages := Seq(scalaJSPipeline)
      )
      .enablePlugins(SbtWebScalaJSEsbuildPlugin)
     
    lazy val client = project.enablePlugins(ScalaJSEsbuildPlugin)

See examples for project templates.

Package managers

Plugins use npm by default, but provided PackageManager abstraction allows configuration of other package managers.

// for yarn
esbuildPackageManager := new PackageManager {
  override def name = "yarn"
  override def lockFile = "yarn.lock"
  override def installCommand = "install"
}

// for pnpm
esbuildPackageManager := new PackageManager {
  override def name = "pnpm"
  override def lockFile = "pnpm-lock.yaml"
  override def installCommand = "install"
}

License

This software is licensed under the MIT license