Typescript Packaging & Publishing

This page will discuss common topics around publishing your Typescript projects for others to use.

Simple publish

build.mill (download, browse)
package build

import mill._, javascriptlib._

object foo extends PublishModule {
  def publishMeta = Task {
    PublishMeta(
      name = "mill-simple",
      version = "1.0.0",
      description = "A simple Node.js command-line tool",
      files = Seq("README.md"),
      bin = Map(
        "greet" -> "src/foo.js"
      )
    )
  }
}

You’ll need to define some metadata in the publishMeta tasks. This metadata is roughly equivalent to what you’d define in a package.json file.

Important package.json info required for publishing are auto-magically generated. main file is by default the file defined in the mainFileName task, it can be modified if needed.

Use the .npmrc file to include authentication tokens for publishing to npm or change the regsitry to publish to a private registry.

The package.json generated for this simple publish:

{
 "name": "mill-simple",
 "version": "1.0.0",
 "description": "A simple Node.js command-line tool",
 "license": "MIT",
 "main": "dist/src/foo.js",
 "types": "declarations/src/foo.d.ts",
 "files": ["README.md", "dist", "declarations"],
 "bin": {
   "greet": "./dist/src/foo.js"
 },
 "exports": {
   ".": "./dist/src/foo.js"
 },
 "typesVersions": {
   "*": {
   "./dist/src/foo": ["declarations/src/foo.d.ts"]
   }
 }
}
> npm i -g mill-simple # install the executable file globally
...

> greet "James Bond"
Hello James Bond!

Realistic publish

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 PublishModule {
  def moduleDeps = Seq(foo, foo.bar)

  def generatedSources = Task {
    os.write(
      Task.dest / "qux.generated.ts",
      s"""export default class QuxGen {
  static value: number = 123
}
      """.stripMargin
    )

    Seq(PathRef(Task.dest))
  }

  def exports = Map(
    "./qux/generate_user" -> "src/generate_user.js",
    "./foo" -> "foo/src/foo.js",
    "./foo/bar" -> "foo/bar/src/bar.js"
  )

  def publishMeta = PublishMeta(
    name = "mill-realistic",
    version = "1.0.3",
    description = "A simple Node.js command-line tool",
    files = Seq("README.md"),
    bin = Map(
      "qux" -> "src/qux.js"
    )
  )

  object test extends TypeScriptTests with TestModule.Jest
}

In this example, we define multiple exports for our application with the export task The package.json generated for this lib publish:

{
 "name": "mill-realistic",
 "version": "1.0.3",
 "description": "A simple Node.js command-line tool",
 "license": "MIT",
 "main": "dist/src/qux.js",
 "types": "declarations/src/qux.d.ts",
 "files": [
   "README.md",
   "dist",
   "declarations"
 ],
 "bin": {
   "qux": "./dist/src/qux.js"
 },
 "dependencies": {
   "immutable": "4.3.7"
 },
 "devDependencies": {
   "immutable": "4.3.7"
 },
 "exports": {
   ".": "./dist/src/qux.js",
   "./qux/generate_user": "./dist/src/generate_user.js",
   "./foo": "./dist/foo/src/foo.js",
   "./foo/bar": "./dist/foo/bar/src/bar.js"
 },
 "typesVersions": {
   "*": { ... }
 }
}
> mill qux.test
PASS .../qux.test.ts
...

> npm i -g mill-realistic
...

> qux James Bond prof
{ prof: 'Professor' }
prof
Professor
Hello James Bond Professor