How to share code
If you want to distribute code written on top of ScalablyTyped, so there is a special workflow for producing free-standing libraries based on the generated code.
Example project
Have a look at mobx-slinky to get a taste of what's possible. It's published through bintray so you can test it in any Scala.js project.
Setup
Requirements
This plugin needs sbt 1.8.x.
Since we generate source code, it should work with any combination of Scala 2.12 / 2.13 / 3.2.0 and Scala.js 1.11+
Starting from 1.0.0-beta30, it is in many cases not possible to compile the generated code if you have the macro paradise plugin enabled, because of some old lingering bugs. So don't do that, it's a dead end anyway.
project/plugins.sbt
Add to your addSbtPlugin("org.scalablytyped.converter" % "sbt-converter" % "1.0.0-beta44")
build.sbt
:
Activate the plugin for a project in your project.enablePlugins(ScalablyTypedConverterGenSourcePlugin)
Setup your npm packages
project.settings(
Compile / npmDependencies ++= Seq(
"react-router-dom" -> "5.1.2",
"@types/react-router-dom" -> "5.1.2"
)
)
Note that some packages contain first-party typescript type definitions,
while for others like react-router-dom
we need to get separate @types
packages.
These are originally from DefinitelyTyped.
Version numbers and @types
packages are probably most conveniently found at npmjs.com.
Choose your flavour (optional)
We have put a lot of effort into top-notch interop with two well-known Scala.js react wrapper libraries. If you want to use either make sure to choose the corresponding flavour.
Slinky
project.settings(
// for Slinky web projects
stFlavour := Flavour.Slinky,
// for Slinky native projects
stFlavour := Flavour.SlinkyNative,
)
Scalajs-react
project.settings(
stFlavour := Flavour.Japgolly
)
Use yarn instead of npm
The plugin checks for updated npm dependencies on each compile, and yarn responds much faster than npm.
Configure scalajs-bundler like this:
project.settings(
useYarn := true
)
Yarn will need to be present on your system for this to work. You should also check in yarn.lock
.
How it works
The conversion process itself is the same as the normal plugin, but the development process differs a bit:
It supports minimizing transitive dependencies. You typically want to focus on one library. The fact that it may use node, std, or whatever else is probably secondary. You might even want to break such dependency chain explicitly for your users, so that all references to such libraries will end up as
js.Any
You'll compile all the generated code completely normally as part of your build. The reason why this is beneficial for a distributing a library is that it'll be completely free-standing with no dependency on the plugin.
stSourceGenMode
)
Two different modes (stSourceGenMode := SourceGenMode.ResourceGenerator
(the default)
In this mode the plugin works as a source generator in sbt, and the source code will be placed in
target/scala-2.13/src_managed/scalablytyped
(or similar)
stSourceGenMode := SourceGenMode.Manual(toDir: File, toDirOverrides: Map[String, File] = Map.empty)
In this mode the plugin will only run when you runstImport
manually, and you specify which directory the source files will be placed in. Note that the plugin will delete any other files in this folder,
You can also optionally provide toDirOverrides
if you want to split the generated files into multiple source folders.
Typically, you'll use this to publish multiple projects instead of one.
Note that it's your responsibility to split things into projects in such a manner that the dependency graph is maintained.
For that reason it's likely you'll want to extract leaf nodes in the dependency graph.
The key in the map is the typescript name of a library, for instance @mui/base
or typescript
.
Compiling all that generated code
There may be a lot.
Ignoring libraries or modules (stIgnore) will help, but our main tool here is called stMinimize
.
By minimization we mean to remove everything from a library except what is referenced from somewhere else, so that things keep compiling.
The point is that you typically want everything from your main libraries, but you care less about their (transitive) dependencies.
Since you typically don't want to enumerate all transitive dependencies, the Selection
helper type provided for this:
Selection.None
disables for all libraries (default)Selection.NoneExcept(String*)
enables only given librariesSelection.All
enables for all librariesSelection.AllExcept(String*)
enables only not given libraries (most useful)
Using that, typical usage of stMinimize
looks like this:
project.settings(
Compile / stMinimize := Selection.AllExcept("axios", "material-ui", "mobx-react", "mobx")
)
Note that if you use a react flavour which generates a components
package, all those
components are considered "entry points", and are not eligible for removal.
In other words, if you use only react components from a library, it's fine to minimize that, too.
stMinimizeKeep
If you want to just keep a few things from a library and minimize away the rest, there is also a mechanism for that.
The names you supply should be exactly as they appear in the generated scala code without the initial package prefix (typically typings
).
project.settings(
/* setup libraries */
Compile / npmDependencies ++= Seq(
"moment" -> "2.24.0",
"react-big-calendar" -> "0.22",
"@types/react-big-calendar" -> "0.22.3"
),
/* say we want to minimize all */
stMinimize := Selection.All,
/* but keep these very specific things*/
stMinimizeKeep ++= List(
"moment.mod.^",
"reactBigCalendar.mod.momentLocalizer",
"reactBigCalendar.mod.View",
),
)
Recommended settings
With stUseScalaJsDom
set to true, stMinimize
which removes most unnecessary code
you should now have a reasonably small amount of code to compile.
With stOutputPackage
it'll be shaded into another package so it won't clash with
other usage of ScalablyTyped.
A this point you can start adding your own code if wanted, and publish libraries completely free-standing libraries.
If you are only packaging a facade generated from scalablytyped, without any custom code, you are likely better by excluding the javadocs on the library package, which can be done with (sonatype requires the docs artifact to be uploaded even if it is empty):
sources in (Compile, doc) := Nil,
Troubleshooting
If you get Stackoverflow errors from Scala 2, make sure that the -Ymacro-annotations
compiler option is not enabled, as it commonly causes issues on library development.