Building Javascript with Mill
This page contains a quick introduction to getting start with using Mill to build a simple Typescript program. It walks through a series of Mill builds of increasing complexity to show you the key features and usage of the Mill build tool. The other pages of this section of the docs go into more depth into individual Mill features. These aren’t intended to be read comprehensively top-to-bottom, but rather looked up when you have a particular interest e.g. in testing, publishing, and so on.
The API reference for Mill’s Typescript toolchain can be found at:
If you aren’t sure why you would use Mill, see Why Use Mill? for a discussion on the motivations of the project. If you are migrating an existing project, see Migrating to Mill.
Simple Typescript Module
package build
import mill.*, javascriptlib.*
object foo extends TypeScriptModule {
def npmDeps = Seq("immutable@4.3.7")
object test extends TypeScriptTests, 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
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
package build
import mill.*, javascriptlib.*
object foo extends TypeScriptModule {
/** Total number of lines in module source files */
def lineCount: T[Int] = Task {
sources()
.flatMap(pathRef => os.walk(pathRef.path))
.filter(_.ext == "ts")
.map(os.read.lines(_).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, 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
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, 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
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, 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
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, 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!