Building Javascript with Mill

Simple Typescript Module

build.mill (download, browse)
package build

import mill._, javascriptlib._

object foo extends TypeScriptModule {
  def npmDeps = Seq("immutable@4.3.7")
  object test extends TypeScriptTests with TestModule.Jest
}

Documentation for mill.example.javascriptlib

> mill foo.run James Bond prof
Hello James Bond Professor

> mill foo.test
PASS .../foo.test.ts
...
Test Suites:...1 passed, 1 total...
Tests:...3 passed, 3 total...
...

> mill show foo.bundle
Build succeeded!

> node out/foo/bundle.dest/bundle.js James Bond prof
Hello James Bond Professor

React Module

build.mill (download, browse)
package build

import mill._, javascriptlib._

object foo extends RsWithServeModule {
  override def forkEnv = super.forkEnv() + ("PORT" -> "3000")

}

Documentation for mill.example.javascriptlib

> mill foo.test
PASS src/test/App.test.tsx
...renders learn react link...
Test Suites:...1 passed, 1 total
Tests:...1 passed, 1 total
Snapshots:...
Time:...
Ran all test suites...

> mill show foo.bundle # build the react app with react-scripts # `foo.run` serves static html using serve
...

Custom Build Logic

build.mill (download, browse)
package build

import mill._, javascriptlib._

object foo extends TypeScriptModule {

  /** Total number of lines in module source files */
  def lineCount = Task {
    allSources().map(f => os.read.lines(f.path).size).sum
  }

  /** Generate resource using lineCount of sources */
  def resources = Task {
    os.write(Task.dest / "line-count.txt", "" + lineCount())
    super.resources() ++ Seq(PathRef(Task.dest))
  }

  object test extends TypeScriptTests with TestModule.Jest

}

Documentation for mill.example.javascriptlib

> mill foo.run
Line Count: 21

> mill show foo.bundle
Build succeeded!

> node out/foo/bundle.dest/bundle.js
Line Count: 21

Multi Module Project

build.mill (download, browse)
package build

import mill._, javascriptlib._

object foo extends TypeScriptModule {
  object bar extends TypeScriptModule {
    def npmDeps = Seq("immutable@4.3.7")
  }

}

object qux extends TypeScriptModule {
  def moduleDeps = Seq(foo, foo.bar)
  object test extends TypeScriptTests with TestModule.Jest
}

Documentation for mill.example.javascriptlib This example demonstrates inter-related typescript modules

> mill qux.run James Bond prof
Hello James Bond Professor

> mill show qux.bundle
Build succeeded!

> node out/qux/bundle.dest/bundle.js James Bond prof
Hello James Bond Professor

> mill qux.test
PASS .../qux.test.ts
...

Simple Client-Server

build.mill (download, browse)
package build
import mill._, javascriptlib._

object client extends ReactScriptsModule

object server extends TypeScriptModule {

  /** Bundle client as resource */
  def resources = Task {
    os.copy(client.bundle().path, Task.dest / "build")
    super.resources() ++ Seq(PathRef(Task.dest))
  }

  override def forkEnv = super.forkEnv() + ("PORT" -> "3000")

  object test extends TypeScriptTests with TestModule.Jest
}

Documentation for mill.example.javascriptlib This example demonstrated a js client module and js server module, wired up and interacting Runninng mill server.run serves bundled static HTML files through the server.

> mill client.test
PASS .../App.test.tsx
...

> mill client.bundle   # bundle the react app;
...

> mill show server.bundle # bundle the node server
...
Build succeeded!

Realistic Client-Server Example Project

build.mill (download, browse)
package build
import mill._, javascriptlib._

object client extends ReactScriptsModule

object server extends TypeScriptModule {
  def npmDeps =
    Seq("@types/cors@^2.8.17", "@types/express@^5.0.0", "cors@^2.8.5", "express@^4.21.1")

  def npmDevDeps =
    super.npmDevDeps() ++ Seq("@types/supertest@^6.0.2", "supertest@^7.0.0")

  def bundleExternal = super.bundleExternal() ++ Seq(ujson.Str("express"), ujson.Str("cors"))

  def forkEnv = super.forkEnv() + ("PORT" -> "3001")

  /** Bundle client as resource */
  def resources = Task {
    os.copy(client.bundle().path, Task.dest / "build")
    super.resources() ++ Seq(PathRef(Task.dest))
  }

  object test extends TypeScriptTests with TestModule.Jest
}

Documentation for mill.example.javascriptlib

This example demonstrates how to set up a simple React web app running on an Express JS server implementing the popular Todo-MVC demo application. It includes a test suite for both client and server modules. Runninng mill app.run serves bundled static HTML files through the server.

> mill client.test
PASS src/test/App.test.tsx
...

> mill client.bundle   # bundle the react app;
...

> mill server.test
PASS .../server.test.ts
...
Test Suites:...1 passed, 1 total...
Tests:...3 passed, 3 total...
...

> mill show server.bundle # bundle the express server
...
Build succeeded!