Repository: scalacenter/scalajs-bundler Branch: main Commit: 2d9cbce21781 Files: 193 Total size: 276.1 KB Directory structure: gitextract_pvw7avpo/ ├── .drone.yml ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ ├── ci.yml │ ├── ghpages.yml │ └── release.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.sbt ├── manual/ │ └── src/ │ ├── ornate/ │ │ ├── changelog.md │ │ ├── community.md │ │ ├── cookbook.md │ │ ├── getting-started.md │ │ ├── index.md │ │ ├── motivation.md │ │ └── reference.md │ └── ornate.conf ├── project/ │ ├── build.properties │ └── plugins.sbt ├── sbt-scalajs-bundler/ │ └── src/ │ ├── main/ │ │ └── scala/ │ │ └── scalajsbundler/ │ │ ├── BundlerFile.scala │ │ ├── BundlerFileType.scala │ │ ├── BundlingMode.scala │ │ ├── ExternalCommand.scala │ │ ├── JSDOMNodeJSEnv.scala │ │ ├── JsDomTestEntries.scala │ │ ├── NpmDependencies.scala │ │ ├── NpmPackage.scala │ │ ├── PackageJson.scala │ │ ├── Stats.scala │ │ ├── Webpack.scala │ │ ├── WebpackDevServer.scala │ │ ├── WebpackEntryPoint.scala │ │ ├── sbtplugin/ │ │ │ ├── LibraryTasks.scala │ │ │ ├── NpmUpdateTasks.scala │ │ │ ├── PackageJsonTasks.scala │ │ │ ├── SBTBundlerFile.scala │ │ │ ├── ScalaJSBundlerPlugin.scala │ │ │ ├── Settings.scala │ │ │ └── WebpackTasks.scala │ │ └── util/ │ │ ├── CachedBundleFiles.scala │ │ ├── Caching.scala │ │ ├── Commands.scala │ │ ├── JS.scala │ │ ├── JSBundler.scala │ │ ├── JSPrinters.scala │ │ ├── JSTrees.scala │ │ └── ScalaJSNativeLibraries.scala │ └── sbt-test/ │ └── sbt-scalajs-bundler/ │ ├── additonalNpmConfig/ │ │ ├── build.sbt │ │ ├── project/ │ │ │ └── plugins.sbt │ │ └── test │ ├── browserless/ │ │ ├── README.md │ │ ├── build.sbt │ │ ├── project/ │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ └── scala/ │ │ │ │ ├── example/ │ │ │ │ │ ├── Foo.scala │ │ │ │ │ └── Main.scala │ │ │ │ └── uuid/ │ │ │ │ └── uuid.scala │ │ │ └── test/ │ │ │ └── scala/ │ │ │ └── example/ │ │ │ └── FooTest.scala │ │ └── test │ ├── custom-test-config/ │ │ ├── README.md │ │ ├── build.sbt │ │ ├── project/ │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ └── scala/ │ │ │ │ └── example/ │ │ │ │ └── Component.scala │ │ │ └── test/ │ │ │ └── scala/ │ │ │ └── example/ │ │ │ └── SomeTest.scala │ │ ├── test │ │ └── test.webpack.config.js │ ├── facade/ │ │ ├── build.sbt │ │ ├── facade/ │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── scala/ │ │ │ └── uuid/ │ │ │ └── uuid.scala │ │ ├── project/ │ │ │ └── plugins.sbt │ │ ├── test │ │ └── usage/ │ │ └── src/ │ │ ├── main/ │ │ │ └── scala/ │ │ │ └── example/ │ │ │ ├── Foo.scala │ │ │ └── Main.scala │ │ └── test/ │ │ └── scala/ │ │ └── example/ │ │ └── FooTest.scala │ ├── facade-examples/ │ │ ├── build.sbt │ │ ├── project/ │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ ├── resources/ │ │ │ │ │ ├── class.js │ │ │ │ │ ├── foo.js │ │ │ │ │ └── function.js │ │ │ │ └── scala/ │ │ │ │ └── example/ │ │ │ │ ├── Main.scala │ │ │ │ └── facades.scala │ │ │ └── test/ │ │ │ └── scala/ │ │ │ └── example/ │ │ │ └── FacadesTest.scala │ │ └── test │ ├── generated-sources/ │ │ ├── build.sbt │ │ ├── project/ │ │ │ └── plugins.sbt │ │ └── test │ ├── global-namespace-with-jsdom-unit-testing/ │ │ ├── build.sbt │ │ ├── common.webpack.config.js │ │ ├── dev.webpack.config.js │ │ ├── project/ │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ └── scala/ │ │ │ │ └── example/ │ │ │ │ └── Main.scala │ │ │ └── test/ │ │ │ └── scala/ │ │ │ └── example/ │ │ │ └── MainTest.scala │ │ ├── test │ │ └── test.webpack.config.js │ ├── js-resources/ │ │ ├── build.sbt │ │ ├── project/ │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ ├── resources/ │ │ │ │ │ └── my-module.js │ │ │ │ └── scala/ │ │ │ │ └── example/ │ │ │ │ ├── Main.scala │ │ │ │ └── MyModule.scala │ │ │ └── test/ │ │ │ └── scala/ │ │ │ └── example/ │ │ │ └── MyModuleTest.scala │ │ └── test │ ├── js-source-directory/ │ │ ├── build.sbt │ │ ├── project/ │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ ├── js/ │ │ │ │ │ ├── config.json │ │ │ │ │ ├── my-module.js │ │ │ │ │ └── nested/ │ │ │ │ │ └── config2.json │ │ │ │ └── scala/ │ │ │ │ └── example/ │ │ │ │ ├── Main.scala │ │ │ │ └── MyModule.scala │ │ │ └── test/ │ │ │ └── scala/ │ │ │ └── example/ │ │ │ └── MyModuleTest.scala │ │ └── test │ ├── library/ │ │ ├── build.sbt │ │ ├── index.html │ │ ├── project/ │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ └── main/ │ │ │ └── scala/ │ │ │ ├── example/ │ │ │ │ └── Library.scala │ │ │ └── uuid/ │ │ │ └── uuid.scala │ │ └── test │ ├── newer-linker/ │ │ ├── build.sbt │ │ ├── project/ │ │ │ ├── newer-scala-js.sbt │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ └── test/ │ │ │ └── scala/ │ │ │ └── example/ │ │ │ └── NewerLinkerTest.scala │ │ └── test │ ├── sbt-1.8/ │ │ ├── build.sbt │ │ ├── project/ │ │ │ ├── build.properties │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ └── test/ │ │ │ └── scala/ │ │ │ └── example/ │ │ │ └── BasicTest.scala │ │ └── test │ ├── sharedconfig/ │ │ ├── README.md │ │ ├── build.sbt │ │ ├── common.webpack.config.js │ │ ├── dev.webpack.config.js │ │ ├── index-prod.html │ │ ├── index.html │ │ ├── prod.webpack.config.js │ │ ├── project/ │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ └── scala/ │ │ │ │ ├── example/ │ │ │ │ │ └── Main.scala │ │ │ │ └── leaflet/ │ │ │ │ └── modules/ │ │ │ │ └── modules.scala │ │ │ └── test/ │ │ │ └── scala/ │ │ │ └── example/ │ │ │ └── SomeTest.scala │ │ └── test │ ├── static/ │ │ ├── README.md │ │ ├── build.sbt │ │ ├── index-prod.html │ │ ├── index.html │ │ ├── project/ │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ └── scala/ │ │ │ │ ├── example/ │ │ │ │ │ └── Main.scala │ │ │ │ └── snabbdom/ │ │ │ │ ├── modules/ │ │ │ │ │ └── modules.scala │ │ │ │ └── snabbdom.scala │ │ │ └── test/ │ │ │ └── scala/ │ │ │ └── example/ │ │ │ └── SomeTest.scala │ │ └── test │ ├── transitive/ │ │ ├── build.sbt │ │ ├── project/ │ │ │ └── plugins.sbt │ │ └── test │ ├── webpack-assets/ │ │ ├── README.md │ │ ├── badconfig1.js │ │ ├── badconfig2.js │ │ ├── build.sbt │ │ ├── dev.config.js │ │ ├── prod.config.js │ │ ├── project/ │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ └── main/ │ │ │ ├── resources/ │ │ │ │ ├── entry.js │ │ │ │ └── styles.css │ │ │ └── scala/ │ │ │ └── example/ │ │ │ └── Main.scala │ │ └── test │ ├── webpack-assets-cookbook/ │ │ ├── README.md │ │ ├── build.sbt │ │ ├── project/ │ │ │ ├── ZipHelper.scala │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ └── scala/ │ │ │ │ └── example/ │ │ │ │ └── Main.scala │ │ │ └── universal/ │ │ │ └── index.html │ │ └── test │ ├── webpack-dev-server/ │ │ ├── build.sbt │ │ ├── project/ │ │ │ └── plugins.sbt │ │ ├── src/ │ │ │ └── main/ │ │ │ └── scala/ │ │ │ └── example/ │ │ │ └── Main.scala │ │ ├── test │ │ └── webpack.config.js │ └── yarn-interactive/ │ ├── build.sbt │ ├── project/ │ │ └── plugins.sbt │ ├── src/ │ │ └── main/ │ │ └── scala/ │ │ └── example/ │ │ └── Main.scala │ └── test ├── sbt-web-scalajs-bundler/ │ └── src/ │ ├── main/ │ │ └── scala/ │ │ └── scalajsbundler/ │ │ └── sbtplugin/ │ │ ├── NpmAssets.scala │ │ └── WebScalaJSBundlerPlugin.scala │ └── sbt-test/ │ └── sbt-web-scalajs-bundler/ │ └── play/ │ ├── build.sbt │ ├── client/ │ │ └── src/ │ │ └── main/ │ │ └── scala/ │ │ ├── example/ │ │ │ └── Main.scala │ │ └── snabbdom/ │ │ ├── modules/ │ │ │ └── modules.scala │ │ └── snabbdom.scala │ ├── project/ │ │ └── plugins.sbt │ ├── server/ │ │ └── src/ │ │ ├── main/ │ │ │ ├── resources/ │ │ │ │ ├── application.conf │ │ │ │ └── router.routes │ │ │ └── scala/ │ │ │ └── example/ │ │ │ ├── ExampleController.scala │ │ │ └── Loader.scala │ │ └── test/ │ │ ├── resources/ │ │ │ └── logback.xml │ │ └── scala/ │ │ └── example/ │ │ └── ExampleSpec.scala │ └── test └── scalajs-bundler-linker/ └── src/ └── main/ └── scala/ └── scalajsbundler/ └── bundlerlinker/ ├── BundlerLinkerImpl.scala └── EntryPointAnalyzerBackend.scala ================================================ FILE CONTENTS ================================================ ================================================ FILE: .drone.yml ================================================ build: image: julienrf/docker-scala-sbt-git commands: - sbt clean test scripted cache: mount: - .git - /drone/.ivy2 - /drone/.sbt ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: push: branches: - main pull_request: branches: - main jobs: build: strategy: matrix: os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: coursier/cache-action@v5 - uses: actions/setup-java@v4 with: java-version: "11" distribution: "adopt" - uses: actions/setup-node@v4 with: node-version: 16.14.2 - name: Setup yarn run: npm install -g yarn@1.22.15 - name: Unit tests run: sbt test - name: Scripted tests run: sbt scripted - name: Build the manual run: sbt manual/makeSite ================================================ FILE: .github/workflows/ghpages.yml ================================================ name: Build and Deploy GhPages docs on: push: branches: - main jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-java@v4 with: java-version: "11" distribution: "adopt" - uses: coursier/cache-action@v5 - name: Build run: sbt manual/makeSite - name: Deploy uses: JamesIves/github-pages-deploy-action@3.6.2 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH: gh-pages FOLDER: manual/target/site ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: branches: [main] tags: ["*"] jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: # fetches all tags, required to correctly set the version fetch-depth: 0 - uses: actions/setup-java@v4 with: java-version: "8" distribution: "adopt" - uses: olafurpg/setup-gpg@v3 - uses: coursier/cache-action@v5 - name: Publish run: sbt clean ci-release env: PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} PGP_SECRET: ${{ secrets.PGP_SECRET }} SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} CI_RELEASE: "; scalajs-bundler-linker/publishSigned; sbt-scalajs-bundler/publishSigned; sbt-web-scalajs-bundler/publishSigned" CI_SNAPSHOT_RELEASE: "; scalajs-bundler-linker/publish; sbt-scalajs-bundler/publish; sbt-web-scalajs-bundler/publish" ================================================ FILE: .gitignore ================================================ target/ .idea local.* ================================================ FILE: CONTRIBUTING.md ================================================ Contributing =========== ## General recommendations Test and document each added feature. ## Organization of the repository - `sbt-scalajs-bundler/` The scalajs-bundler sbt plugin - `sbt-web-scalajs-bundler/` sbt plugin for integrating scalajs-bundler and [sbt-web-scalajs](https://github.com/vmunier/sbt-web-scalajs) - `manual/` Sources of the documentation ## Build the project ~~~ sh $ sbt package ~~~ ## Run the tests We use [sbt-scripted](http://eed3si9n.com/testing-sbt-plugins) to test the plugins. However, we customized the way tests are launched to filter tests according to their compatibility with major versions of sbt and Scala.js: - tests can have a `project/build.properties` file defining a specific sbt version they are compatible with, To run all the tests: ~~~ sh $ sbt scripted ~~~ To run a single test: ~~~ sh $ sbt "sbt-scalajs-bundler/scripted sbt-scalajs-bundler/" ~~~ (where `` is replaced by one of the [tests](https://github.com/scalacenter/scalajs-bundler/tree/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler)). Sometimes you would like to open an interactive sbt shell and manually play with sbt tasks instead of writing them into a sbt-scripted test. In such a case, you can start with an existing sbt-scripted test and add the following commands at the top of its `test` file: ~~~ $ pause $ fail ~~~ Then, when you will run the scripted task on this this test, it will start by making a pause. You can then open a new sbt shell in the running test: ~~~ $ cd /tmp/sbt_fa1e13d43/test-name $ sbt -Dplugin.version=x.y.z-SNAPSHOT ~~~ (where `x.y.z` is replaced by the current version of sbt-scalajs-bundler) ## Publish locally ~~~ sh $ sbt publishLocal ~~~ ## Preview the documentation ~~~ sh $ sbt manual/previewSite ~~~ ## Publish a release - Check that the `changelog.md` file is up to date (in the `manual` project) - Push a Git tag (name it `vX.Y.Z`, where `X`, `Y`, `Z` are major, minor and revision numbers, respectively) - Either from GitHub [web interface](https://github.com/scalacenter/scalajs-bundler/releases/new), - Or from the Git CLI: `$ git tag v1.0.0; git push --tags` ================================================ FILE: LICENSE ================================================ scalajs-bundler is licensed under the [BSD 3-Clause License](http://opensource.org/licenses/BSD-3-Clause). Copyright (c) 2016 EPFL All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the EPFL nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ scalajs-bundler [![](https://index.scala-lang.org/scalacenter/scalajs-bundler/sbt-scalajs-bundler/latest.svg)](https://index.scala-lang.org/scalacenter/scalajs-bundler) [![Gitter](https://img.shields.io/badge/gitter-join%20chat-green.svg)](https://gitter.im/scalacenter/scalajs-bundler) [![CI](https://github.com/scalacenter/scalajs-bundler/actions/workflows/ci.yml/badge.svg)](https://github.com/scalacenter/scalajs-bundler/actions/workflows/ci.yml) ============== Module bundler for Scala.js projects that use NPM packages. Uses [npm](https://www.npmjs.com) and [webpack](https://webpack.github.io/) under the hood. ## Documentation More information on how to use it in the [documentation](https://scalacenter.github.io/scalajs-bundler). ## Contributing scalajs-bundler is community-maintained. Contributions are welcome! See the [open issues](https://github.com/scalacenter/scalajs-bundler/issues) and the [CONTRIBUTING.md](CONTRIBUTING.md) file. ## License This content is released under the [BSD 3-Clause License](http://opensource.org/licenses/BSD-3-Clause). ================================================ FILE: build.sbt ================================================ val scalaJSVersion = sys.env.getOrElse("SCALAJS_VERSION", "1.3.0") lazy val `scalajs-bundler-linker` = project.in(file("scalajs-bundler-linker")) .settings( scalaVersion := "2.12.11", libraryDependencies += "org.scala-js" %% "scalajs-linker" % scalaJSVersion ) val `sbt-scalajs-bundler` = project.in(file("sbt-scalajs-bundler")) .enablePlugins(SbtPlugin, BuildInfoPlugin) .settings(commonSettings) .settings( description := "Module bundler for Scala.js projects", libraryDependencies += "com.google.jimfs" % "jimfs" % "1.2", libraryDependencies += "com.typesafe.play" %% "play-json" % "2.9.4", addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion), buildInfoKeys := Seq[BuildInfoKey](version), buildInfoPackage := "scalajsbundler.sbtplugin.internal", // When supported, add: buildInfoOptions += sbtbuildinfo.BuildInfoOption.PackagePrivate scriptedDependencies := { val () = scriptedDependencies.value val () = publishLocal.value val () = (`scalajs-bundler-linker` / publishLocal).value }, ) val `sbt-web-scalajs-bundler` = project.in(file("sbt-web-scalajs-bundler")) .enablePlugins(SbtPlugin) .settings(commonSettings) .settings( // sbt-web-scalajs does not support sbt 1.2.x scriptedDependencies := { val () = scriptedDependencies.value val () = publishLocal.value val () = (`sbt-scalajs-bundler` / publishLocal).value val () = (`scalajs-bundler-linker` / publishLocal).value }, description := "Module bundler for Scala.js projects (integration with sbt-web-scalajs)", addSbtPlugin("com.vmunier" % "sbt-web-scalajs" % "1.1.0") ) .dependsOn(`sbt-scalajs-bundler`) // Dummy project that exists just for the purpose of aggregating the two sbt // plugins. I can not do that in the `doc` project below because the // scalaVersion is not compatible. val apiDoc = project.in(file("api-doc")) .enablePlugins(ScalaUnidocPlugin) .settings(noPublishSettings: _*) .settings( (ScalaUnidoc / unidoc / scalacOptions) ++= Seq( "-groups", "-doc-source-url", s"https://github.com/scalacenter/scalajs-bundler/blob/v${version.value}€{FILE_PATH}.scala", "-sourcepath", (ThisBuild / baseDirectory).value.absolutePath ), (ScalaUnidoc / unidoc / unidocProjectFilter) := inAnyProject -- inProjects(`scalajs-bundler-linker`) ) .aggregate(`sbt-scalajs-bundler`, `sbt-web-scalajs-bundler`) val ornateTarget = Def.setting(target.value / "ornate") val manual = project.in(file("manual")) .enablePlugins(OrnatePlugin) .settings(noPublishSettings: _*) .settings( scalaVersion := "2.12.11", ornateSourceDir := Some(sourceDirectory.value / "ornate"), ornateTargetDir := Some(ornateTarget.value), ornateSettings := Map("version" -> version.value), ornate / siteSubdirName := "", addMappingsToSiteDir(ornate / mappings, ornate / siteSubdirName), ornate / mappings := { val _ = ornate.value val output = ornateTarget.value output ** AllPassFilter --- output pair Path.relativeTo(output) }, packageDoc / siteSubdirName := "api/latest", addMappingsToSiteDir(mappings in ScalaUnidoc in packageDoc in apiDoc, packageDoc / siteSubdirName) ) val `scalajs-bundler` = project.in(file(".")) .settings(noPublishSettings: _*) .aggregate(`sbt-scalajs-bundler`, `sbt-web-scalajs-bundler`) inThisBuild(List( scalacOptions ++= Seq( "-feature", "-deprecation", "-encoding", "UTF-8", "-unchecked", "-Xlint", "-Yno-adapted-args", "-Ywarn-dead-code", "-Ywarn-numeric-widen", "-Ywarn-value-discard", "-Xfuture" ), scmInfo := Some( ScmInfo( url("https://github.com/scalacenter/scalajs-bundler"), "scm:git@github.com:scalacenter/scalajs-bundler.git" ) ), organization := "ch.epfl.scala", homepage := Some(url(s"https://github.com/scalacenter/scalajs-bundler")), licenses := Seq("MIT License" -> url("http://opensource.org/licenses/mit-license.php")), developers := List(Developer("julienrf", "Julien Richard-Foy", "julien.richard-foy@epfl.ch", url("http://julien.richard-foy.fr"))) )) lazy val commonSettings = List( scriptedLaunchOpts ++= Seq( "-Dplugin.version=" + version.value, s"-Dscalajs.version=$scalaJSVersion", "-Dsbt.execute.extrachecks=true" // Avoid any deadlocks. ), scriptedBufferLog := false, ) lazy val noPublishSettings = Seq( publishArtifact := false, publish := {}, publishLocal := {} ) ThisBuild / ivyLoggingLevel := UpdateLogging.Quiet ================================================ FILE: manual/src/ornate/changelog.md ================================================ # Changelog ## Version 0.20.0 > 2020 October 22 This release upgrades to Scala.js 1.3.0. - Drop support for Scala.js 0.6.x - Drop support for Scala.js 1.0.x through 1.2.x - Add support for Scala.js 1.3.x ## Version 0.19.0 > 2020 October 16 This release drops support for sbt 0.13.x, and adds support for sbt-web-scalajs 1.1.0+. ## Version 0.18.0 > 2020 May 16 This release adds support for scala.js 1.1.0 ## Version 0.17.0 > 2020 Feb 24 The main highlight of this release is better support for Scala.js 1.0.0+ out of the box. We still support Scala.js 0.6.31+ as well. - With Scala.js 1.x, dynamically load the appropriate scalajs-linker, removing the need for the `libraryDependencies += "org.scala-js" %% "scalajs-linker" % scalaVersion` workaround. - With Scala.js 1.x, do not internally link twice when using the `LibraryOnly` and `LibraryAndApplication` bundling modes, improving performance. - With Scala.js 1.x, better integrate with the `jsEnv` and `jsEnvInput` tasks of Scala.js when using `requireJsDomEnv`. You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.16.0...v0.17.0). ## Version 0.16.0 > 2019 Dec 09 The main highlight of this release is the support of Scala.js 1.0.0-RC1 instead of 1.0.0-M7. We still support Scala.js 0.6.x, but we require at least version 0.6.31. - Add support for Scala.js 1.0.0-RC1 (drop support for 1.0.0-M7) - Require Scala.js 0.6.31 or later in the 0.6.x branch - Require sbt 1.2.1 or later in the sbt 1.x branch (sbt 0.13.17+ is still supported) You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.15.0...v0.16.0). ## Version 0.15.0 > 2019 May 21 The main highlight of this release is the support of Scala.js 1.x. We still support Scala.js 0.6.x, but we require at least version 0.6.26. New features: - [#201](https://github.com/scalacenter/scalajs-bundler/issues/201): Introduce a `jsSourceDirectories` setting, which points to a list of directories containing files (`.js`, `.json`, etc.) that can be used by Scala.js projects. - [#246](https://github.com/scalacenter/scalajs-bundler/issues/246): Support Scala.js 1.0.0-M7. You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.14.0...v0.15.0). ## Version 0.14.0 > 2018 Nov 5 This release modifies the `npmUpdate` task and splits the logic into two separate tasks; `npmInstallDependencies` and `npmInstallJSResources`. `npmUpdate` has a less obvious side effect that, not only does it run `npm install`, it would also copy all the JavaScript resources to the `node_modules` directory. This behaviour is fine except that it is not suitable for use in `sourceGenerators` and would cause a cycle in the tasks. `npmInstallDependencies` should be used in cases where you want to want to use a npm module from a sbt task. This fixes the following bugs: - [#258](https://github.com/scalacenter/scalajs-bundler/issues/258): Unable to use npmUpdate in sourceGenerators - [#261](https://github.com/scalacenter/scalajs-bundler/issues/261): Support jsdom v12.x - [#267](https://github.com/scalacenter/scalajs-bundler/issues/267): Support JDK9+ New features: - [#264](https://github.com/scalacenter/scalajs-bundler/issues/264): Ability to set `node` [flags](https://nodejs.org/api/cli.html) - [#266](https://github.com/scalacenter/scalajs-bundler/issues/266): Custom setting for DOM enabled `JSEnv` in `test`. (`requiresDOM` is deprecated) And documentation fixes: - [#269](https://github.com/scalacenter/scalajs-bundler/issues/269): Update docs You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.13.1...v0.14.0). ## Version 0.13.1 > 2018 Jul 13 This fixes the following bugs: - [#224](https://github.com/scalacenter/scalajs-bundler/issues/224): Use project-level custom NPM registry The following PRs are included in this release - [#254](https://github.com/scalacenter/scalajs-bundler/pull/254): Npm/yarn args - [#251](https://github.com/scalacenter/scalajs-bundler/pull/251): Fix typo - [#249](https://github.com/scalacenter/scalajs-bundler/pull/249): Sync yarn.lock between baseDir and installDir You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.13.0...v0.14.1). ## Version 0.13.0 > 2018 Jun 5 This release contains an important revamp on the way webpack support works. Webpack produces a json [stats output](https://webpack.js.org/api/stats) which describes in detail the results of processing your application. Thus, parsing stats we can learn exactly what files are produced rather than guessing. Due to the very nature of parsing the output, and the wide variety of webpack configurations this process may not work in all cases. Please lets us know if you find any issues. **Note:** `Stats` parsing has been tested mostly in Webpack 4. The results may vary with older versions. **Note:** If your webpack produces any kind of std output, parsing stats will likely break. This fixes the following bugs: - [#192](https://github.com/scalacenter/scalajs-bundler/issues/192): Webpack failed to create application bundle - [#111](https://github.com/scalacenter/scalajs-bundler/issues/111): Lots of warnings about source map URLs The following PRs are included in this release - [#247](https://github.com/scalacenter/scalajs-bundler/pull/247): Assets to sbt - [#242](https://github.com/scalacenter/scalajs-bundler/pull/242): Better error display when the webpack call fails - [#241](https://github.com/scalacenter/scalajs-bundler/pull/241): Use Public path from webpack stats - [#240](https://github.com/scalacenter/scalajs-bundler/pull/240): Update concat-with-sourcemaps - [#239](https://github.com/scalacenter/scalajs-bundler/pull/239): Bugfix parsing errors on the output - [#238](https://github.com/scalacenter/scalajs-bundler/pull/238): Fix thread leak - [#237](https://github.com/scalacenter/scalajs-bundler/pull/237): add function as a module example - [#234](https://github.com/scalacenter/scalajs-bundler/pull/234): Read application asset name from stats You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.12.0...v0.13.0). ## Version 0.12.0 > 2018 March 27 - [#223](https://github.com/scalacenter/scalajs-bundler/pull/223): Webpack4 support You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.11.0...v0.12.0). ## Version 0.11.0 > 2018 March 15 - [#228](https://github.com/scalacenter/scalajs-bundler/pull/228): Implements npmUdpdate in a separate object; - [#225](https://github.com/scalacenter/scalajs-bundler/pull/225): Add --mutex to yarn command; - [#222](https://github.com/scalacenter/scalajs-bundler/pull/222): Make library example work in OSX; - [#220](https://github.com/scalacenter/scalajs-bundler/pull/220): Support passing an extra list of arguments to webpack; - [#218](https://github.com/scalacenter/scalajs-bundler/pull/218): Fix [#136](https://github.com/scalacenter/scalajs-bundler/issues/136): Add more precise jsdom detection: - [#216](https://github.com/scalacenter/scalajs-bundler/pull/216): Fix [#200](https://github.com/scalacenter/scalajs-bundler/issues/200): current webpack devserver version does not accept watchOptions; - [#215](https://github.com/scalacenter/scalajs-bundler/pull/215): Fix [#99](https://github.com/scalacenter/scalajs-bundler/issues/99): Relax NPM dependency version conflict resolution; - [#213](https://github.com/scalacenter/scalajs-bundler/pull/213): Fix [#168](https://github.com/scalacenter/scalajs-bundler/issues/168): Update the snabbdom facade for Scala.js 1.0 You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.10.0...v0.11.0). ## Version 0.10.0 > 2018 January 31 This release requires sbt 0.13.16+ and adds support for Scala.js 0.6.22. - [#210](https://github.com/scalacenter/scalajs-bundler/pull/210): Bundler doesn't support version of jsdom more than 9; - [#185](https://github.com/scalacenter/scalajs-bundler/pull/185): Correct webpackBundlingMode key in docs; - [#212](https://github.com/scalacenter/scalajs-bundler/pull/212): Migrate to sbt-scalajs 0.6.22; You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.9.0...v0.10.0). ## Version 0.9.0 > 2017 October 12 When upgrading to this release, make sure to migrate your webpack configuration to [webpack 3.X](https://webpack.js.org/guides/migrating/). This version works with both sbt version 0.13 and 1.0. In order to support sbt 1.0, Scala.js has been upgraded to [0.6.19](https://www.scala-js.org/news/2017/07/29/announcing-scalajs-0.6.19/). - [#175](https://github.com/scalacenter/scalajs-bundler/pull/175): Set webpack 3.X as default version; - [#179](https://github.com/scalacenter/scalajs-bundler/pull/179): Cross publish for sbt 1.0; - [#176](https://github.com/scalacenter/scalajs-bundler/pull/176): Run webpack-dev-server from the scalajs-bundler folder; - [#177](https://github.com/scalacenter/scalajs-bundler/pull/176): Scope webpackBundlingMode per Scala.js stage (`fastOptJS` or `fullOptJS`); You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.8.0...v0.9.0). ## Version 0.8.0 > 2017 September 10 When upgrading to this release, make sure to check out the new [webpackBundlingMode](reference.md#bundling-mode) configuration value. The old `enableReloadWorkflow` key has been removed, in favor of `BundlingMode.LibraryAndApplication()`. - [#143](https://github.com/scalacenter/scalajs-bundler/pull/143): Document the compatible versions of npm; - [#146](https://github.com/scalacenter/scalajs-bundler/pull/146): Document how to use global modules with jsdom in tests; - [#149](https://github.com/scalacenter/scalajs-bundler/pull/149): Introduce `webpackBundlingMode` to finely control whether to bundle the output of Scala.js or not; - [#153](https://github.com/scalacenter/scalajs-bundler/pull/153): Use the non interactive mode of Yarn; - [#161](https://github.com/scalacenter/scalajs-bundler/pull/161): Set `"private": true` in generated `package.json` file to eliminate errors from npm; - [#162](https://github.com/scalacenter/scalajs-bundler/pull/162): Differentiate between missing and unsupported Webpack versions; - [#166](https://github.com/scalacenter/scalajs-bundler/pull/166): Move to Travis-CI; - [#167](https://github.com/scalacenter/scalajs-bundler/pull/167): Upgrade tests that use `uuid`; - [#171](https://github.com/scalacenter/scalajs-bundler/pull/171): Add `scalaJSProjects` resource directories to `monitoredScalaJSDirectories`; - [#172](https://github.com/scalacenter/scalajs-bundler/pull/172): Use `npm install` command instead of `npm update`. You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.7.0...v0.8.0). ## Version 0.7.0 > 2017 July 4 When upgrading to this release, make sure to enable `scalaJSUseMainModuleInitializer := true` in your build if you have a `main` method. - [#125](https://github.com/scalacenter/scalajs-bundler/pull/125): Copy `webpackResources` only if a custom webpack config file is used ; - [#126](https://github.com/scalacenter/scalajs-bundler/pull/126): Ability to add custom `package.json` entries ; - [#129](https://github.com/scalacenter/scalajs-bundler/pull/129): Generate a JavaScript array of webpack entries rather than a string ; - [#140](https://github.com/scalacenter/scalajs-bundler/pull/140): Upgrade to Scala.js 0.6.18 ; - [#141](https://github.com/scalacenter/scalajs-bundler/pull/141): Handle `ImportWithGlobalFallback` in reload workflow. You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.6.0...v0.7.0). ## Version 0.6.0 > 2017 April 26 - [#96](https://github.com/scalacenter/scalajs-bundler/pull/96): webpack-dev-server integration ; - [#102](https://github.com/scalacenter/scalajs-bundler/pull/102): Make it possible to set the version of jsdom and webpack-dev-server to use ; - [#106](https://github.com/scalacenter/scalajs-bundler/pull/106): Add a [Community](community.md) page ; - [#108](https://github.com/scalacenter/scalajs-bundler/pull/108): Add gitter badge to the README ; - [#121](https://github.com/scalacenter/scalajs-bundler/pull/121): Make the sbt task fail when webpack fails ; - [#119](https://github.com/scalacenter/scalajs-bundler/pull/119): Add support for custom webpack config files in tests and in the reload workflow ; You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.5.0...v0.6.0). ## Version 0.5.0 > 2017 January 18 - [#57](https://github.com/scalacenter/scalajs-bundler/pull/57): Webpack 2.x support ; - [#80](https://github.com/scalacenter/scalajs-bundler/pull/80): Upgrade to Scala.js 0.6.14 ; - [#81](https://github.com/scalacenter/scalajs-bundler/pull/81): Disable the reload workflow by default ; - [#94](https://github.com/scalacenter/scalajs-bundler/pull/94): Improve caching of tasks ; - [#95](https://github.com/scalacenter/scalajs-bundler/pull/95): Fix support for spaces in paths ; You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.4.0...v0.5.0). ## Version 0.4.0 > 2016 December 15 - [#53](https://github.com/scalacenter/scalajs-bundler/pull/53): Fix cache invalidation when .js resources change ; - [#54](https://github.com/scalacenter/scalajs-bundler/pull/54): Add support for importing assets from NPM packages ; - [#56](https://github.com/scalacenter/scalajs-bundler/pull/56): Add [Yarn](https://yarnpkg.com/) support ; - [#65](https://github.com/scalacenter/scalajs-bundler/pull/65): Use distinct target directories for `npmUpdate in Compile` and `npmUpdate in Test` ; - [#69](https://github.com/scalacenter/scalajs-bundler/pull/69): Publish the API documentation ; - [#70](https://github.com/scalacenter/scalajs-bundler/pull/70): Ensure that there is no duplicates in NPM dependencies ; - [#71](https://github.com/scalacenter/scalajs-bundler/pull/71): Add a resolution mechanism for conflicting dependencies. You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.3.1...v0.4.0). ## Version 0.3.1 > 2016 December 2 - [#51](https://github.com/scalacenter/scalajs-bundler/pull/51): Support history API within jsdom. You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.3.0...v0.3.1). ## Version 0.3.0 > 2016 November 29 - [#32](https://github.com/scalacenter/scalajs-bundler/pull/32): Add a detailed documentation section on how to write a facade with `@JSImport` (see it [here](cookbook.md#facade)) ; - [#33](https://github.com/scalacenter/scalajs-bundler/pull/33): Fix cache invalidation after custom webpack config file is changed ; - [#35](https://github.com/scalacenter/scalajs-bundler/pull/35): Fix tests on Windows ; - [#37](https://github.com/scalacenter/scalajs-bundler/pull/37): Throw an error if there is no main class ; - [#39](https://github.com/scalacenter/scalajs-bundler/pull/39): Add support for jsdom in tests ; - [#45](https://github.com/scalacenter/scalajs-bundler/pull/45): Forbid `scalaJSModuleKind` to be different from `CommonJSModule` on projects where `ScalaJSBundler` plugin is enabled. You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.2.1...v0.3.0). ## Version 0.2.1 > 2016 November 2 - [#24](https://github.com/scalacenter/scalajs-bundler/pull/24): Fixed npm command in Windows (thanks to [@DylanArnold](https://github.com/DylanArnold)) ; - [#25](https://github.com/scalacenter/scalajs-bundler/pull/25): Fixed the `scalaJSPipeline` task to correctly support source maps. You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.2...v0.2.1). ## Version 0.2 > 2016 November 1 - [#14](https://github.com/scalacenter/scalajs-bundler/pull/14): Improved documentation ; - [#17](https://github.com/scalacenter/scalajs-bundler/pull/17): Faster live-reloading workflow (`fastOptJS::webpack` is ~10x faster) ; - [#18](https://github.com/scalacenter/scalajs-bundler/pull/18): `ScalaJSBundlerPlugin` is not anymore automatically triggered: you have to manually enable it on your projects. `scalaJSModuleKind` is automatically set to `ModuleKind.CommonJSModule` when `ScalaJSBundlerPlugin` is enabled, so you don’t anymore have to set it in your build ; - [#20](https://github.com/scalacenter/scalajs-bundler/pull/20): JavaScript files that are on the classpath can be `@JSImport`ed by your Scala facades. You can find the complete list of commits since the last release [here](https://github.com/scalacenter/scalajs-bundler/compare/v0.1...v0.2). ================================================ FILE: manual/src/ornate/community.md ================================================ # Community - [![Gitter](https://badges.gitter.im/scalacenter/scalajs-bundler.svg)](https://gitter.im/scalacenter/scalajs-bundler?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - [![Stackoverflow](https://img.shields.io/badge/ask-on%20stackoverflow-blue.svg)](http://stackoverflow.com/questions/tagged/scalajs-bundler) - [![Github](https://img.shields.io/badge/contribute-on%20github-green.svg)](https://github.com/scalacenter/scalajs-bundler) ================================================ FILE: manual/src/ornate/cookbook.md ================================================ # Cookbook ![](toctree:local=true,mergeFirst=true) ## How to use a custom webpack configuration file? {#custom-config} First, configure the `webpackConfigFile` setting to refer to your configuration file: ~~~ scala webpackConfigFile in fastOptJS := Some(baseDirectory.value / "my.custom.webpack.config.js") ~~~ Or, if you want to use the same configuration file for both `fastOptJS` and `fullOptJS`: ~~~ scala webpackConfigFile := Some(baseDirectory.value / "my.custom.webpack.config.js") ~~~ Then, you can write your configuration in file `my.custom.webpack.config.js`. We recommend that you reuse the configuration file generated by scalajs-bundler and extend it, rather than writing a configuration file from scratch. You can do so as follows (in file `my.custom.webpack.config.js``): ~~~ javascript var webpack = require('webpack'); module.exports = require('./scalajs.webpack.config'); // And then modify `module.exports` to extend the configuration ~~~ The key part is the `require('./scalajs.webpack.config')`. It loads the configuration file generated by scalajs-bundler so that you can tweak it. It works because your configuration file will be copied into the internal target directory, where the scalajs-bundler generates its configuration file, and where all the npm dependencies have been downloaded (so you can also `require` these dependencies). By default `webpack` task only actually launches webpack if it detects changes in settings or in the custom webpack config file. Depending on your usage scenario, you might want to monitor some other files as well (for example, if your webpack config references some additional resources). This can be achieved by using `webpackMonitoredDirectories` setting: ~~~ scala webpackMonitoredDirectories += baseDirectory.value / "my-scss" includeFilter in webpackMonitoredFiles := "*.scss" ~~~ More fine-grained control over the list of monitored files is possible by overriding the `webpackMonitoredFiles` task. You can find a working example of custom configuration file [here](https://github.com/scalacenter/scalajs-bundler/blob/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/prod.webpack.config.js). It is also possible to configure a webpack config file to be used in reload workflow and when running the tests. This configuration may not contain `entry` and `output` configuration but can be used to configure loaders etc. These configuration files are configured using `webpackConfigFile in reloadTask` or `webpackConfigFile in Test`. For example: ~~~ scala webpackConfigFile in webpackReload := Some(baseDirectory.value / "common.webpack.config.js") webpackConfigFile in Test := Some(baseDirectory.value / "common.webpack.config.js") ~~~ ## Sharing webpack configuration among configuration files {#shared-config} In addition to the configured webpack config file, all .js files in the project base directory (as configured using the `webpackResources` setting) are copied to the target directory so they can be imported from the various configuration files. Here are the steps to share the loader configuration among your prod and dev config files. This uses webpack-merge for convenience. The same result could be accomplished using plain js only. 1. Put configuration in a common.webpack.config.js file: ~~~ javascript module.exports = { module: { loaders: [ ... ], rules: [ ... ] } } ~~~ 2. Add webpack-merge to your `npmDevDependencies`: ~~~ npmDevDependencies in Compile += "webpack-merge" -> "4.1.0" ~~~ 3. Merge in the common configuration in your dev.webpack.js file: ~~~ javascript var merge = require("webpack-merge") var commonConfig = require("./common.webpack.config.js") module.exports = merge(commonConfig, { ... }) ~~~ You can find a working example of a project using a shared configuration file [here](https://github.com/scalacenter/scalajs-bundler/blob/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig). ## How to use npm modules from Scala code? {#facade} Once you have [added npm dependencies](getting-started.md) to the packages you are interested in, you have to *import* them from your code to effectively use them. The recommended way to do that is to: 1. Write a [Scala.js](https://www.scala-js.org/doc/interoperability/facade-types.html) facade annotated with [`@JSImport`](https://www.scala-js.org/doc/interoperability/facade-types.html#a-nameimporta-imports-from-other-javascript-modules) ; 2. Refer to this facade from your code. Let’s illustrate this with an example. Say that you want to write a facade for the following npm module: ~~~ javascript tab="foo.js (CommonJS)" exports.bar = function (i) { return i + 1 }; ~~~ ~~~ javascript tab="foo.js (ES6)" export const bar = i => i + 1; ~~~ The corresponding Scala.js facade looks like the following: ~~~ scala import scala.scalajs.js import scala.scalajs.js.annotation.JSImport @JSImport("foo", JSImport.Namespace) @js.native object foo extends js.Object { def bar(i: Int): Int = js.native } ~~~ There are several points worth highlighting: - The first parameter of the `@JSImport` annotation is the npm module path. This is the value you would pass to the [Nodejs `require`](https://nodejs.org/docs/latest/api/modules.html#modules_all_together) function ; - The second parameter of `@JSImport` is the name of the imported member, or like in our case, `JSImport.Namespace`, to import the whole module instead of just one particular member ; - The facade is concrete. It can either be a Scala `object` or a `class` ; - The facade has a [“JS native” type](https://www.scala-js.org/doc/interoperability/facade-types.html). > {.note} > Other styles of facades (importing a member in particular, importing functions and classes, > importing local JavaScript files, etc.) can be found in > [these tests](https://github.com/scalacenter/scalajs-bundler/blob/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/facade-examples). Finally, in your Scala code, just refer to the `foo` object: ~~~ scala object Main { def main(args: Array[String]): Unit = { println(foo.bar(42)) } } ~~~ ## How to publish a facade for an npm module? {#publish} Create a project for the facade and enable the `ScalaJSBundlerPlugin` as described [here](getting-started.md). Implement the facade as explained in the [above section](cookbook.md#facade). Publish the Scala.js project [as usual](http://www.scala-sbt.org/1.0/docs/Publishing.html). Finally, to use the facade from another Scala.js project, this one needs both to add a dependency on the facade and to enable the `ScalaJSBundlerPlugin` plugin. > {.warning} > Projects that **use** the facade also have to enable the `ScalaJSBundlerPlugin` plugin, > otherwise the dependencies of the facade will not be resolved. ## How to use an existing facade assuming the JS library to be exposed to the global namespace? {#global-namespace} Webpack is able to require external modules by using [imports-loader](https://github.com/webpack-contrib/imports-loader) and expose them to the global namespace by using [expose-loader](https://github.com/webpack/expose-loader). Thus, you can write a custom webpack configuration file that uses this loaders to expose the required modules to the global namespace. Typically, this file will look like this: ~~~ javascript src=../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/global-namespace-with-jsdom-unit-testing/common.webpack.config.js ~~~ Also, tweak your `build.sbt` to add the corresponding NPM dependencies and to use the custom webpack configuration file: ~~~ scala src=../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/global-namespace-with-jsdom-unit-testing/build.sbt#relevant-settings ~~~ You can find a fully working example [here](https://github.com/scalacenter/scalajs-bundler/blob/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/global-namespace-with-jsdom-unit-testing). ## How to bundle an application having several entry points as exports? {#several-entry-points} By default, `ScalaJSBundlerPlugin` assumes that your application only has a main class, activated through `scalaJSUseMainModuleInitializer := true`, and disregards top-level *exports*. If you have exports that need to be exposed as several entry points, this will not work. In such a case, you can use `BundlingMode.LibraryAndApplication()`. `build.sbt`: ~~~ scala src=../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/library/build.sbt#relevant-settings ~~~ Then, assuming that you defined the following library: ~~~ scala src="../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/library/src/main/scala/example/Library.scala#library-definition" ~~~ You can call its methods as follows from your JavaScript code: ~~~ scala src="../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/library/index.html#library-usage" ~~~ ## How to improve the performance of the bundling process? {#performance} You can enable the [library-only bundling mode](reference.md#bundling-mode-library-only) and disable source maps: ~~~ scala webpackBundlingMode := BundlingMode.LibraryOnly() emitSourceMaps := false ~~~ ## How to select specific files from the `BundlingMode.Library` output In [library-only bundling mode](reference.md#bundling-mode-library-only) and [library with application bundling mode](reference.md#bundling-mode-library-and-application), the `webpack` task produces multiple files. In order to determine which of these files is, for instance, the [BundlerFileType.Application](api:scalajsbundler.BundlerFileType$$Application$), you can use the `_.metadata` property of the files, like this: ~~~ scala src="../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/build.sbt#filter-files" ~~~ ## How to rebuild and reload your page on code changes? {#webpack-dev-server} `scalajs-bundler` includes a simple wrapper over webpack-dev-server to simplify your workflow. It is exposed as two stage-level tasks (`startWebpackDevServer` and `stopWebpackDevServer`). The standard work session looks like this: 1. Spawn background server process: ~~~ > fastOptJS::startWebpackDevServer ~~~ By default the server is started on port `8080`. Use `webpackDevServerPort` setting to change this. 2. Instruct SBT to rebuild on source changes: ~~~ > ~fastOptJS ~~~ 3. Now each time you change a source file, Scala.js recompiles it, and webpack-dev-server switches to the updated version. 4. Shut down the background process: ~~~ > fastOptJS::stopWebpackDevServer ~~~ Additional arguments can be passed to webpack-dev-server via `webpackDevServerExtraArgs` setting. For example, you can add the following to your `build.sbt` to make your page reload on every change: ~~~ webpackDevServerExtraArgs := Seq("--inline") ~~~ ## How to pass extra parameters to webpack `scalajs-bundler` invokes `webpack` with a configuration generated either automatically from the build or set with `webpackConfigFile`. `webpack` is then called with the following arguments: ~~~ --config ~~~ You can add extra params to the `webpack` call, for example, to increase debugging ~~~ webpackExtraArgs := Seq("--profile", "--progress", "true") ~~~ **Note** Params are passed verbatim, they are not sanitized and could produce errors when passed to webpack. In particular, don't attempt to override the `--config` param. ## How to use webpack 4 `scalajs-bundler` (version 0.12.0 onwards) supports webpack 4. To enable webpack 4, set the correct versions in `build.sbt` ~~~ scala version in webpack := "4.8.1" version in startWebpackDevServer := "3.1.4" ~~~ Additionally, you need to update any webpack plugins your config uses, to Webpack 4 compatible versions. Webpack 4 has the potential to substantially reduce your webpack compilation times (80% reductions have been observed but your mileage may vary) ## How to get and use a list of assets `scalajs-bundler` (version 0.13.0 onwards) will export a list of all assets produced by webpack. You can read that list on sbt ~~~ scala val files = (webpack in (Compile, fullOptJS)).value ~~~ You can this list e.g. with [sbt-native-packager](https://github.com/sbt/sbt-native-packager)` to add mappings as: ~~~ scala src="../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets-cookbook/build.sbt#scalajs-files" ~~~ This will add all artifacts produced by the fully optimized Scala.JS run to the 'assets' directory of the target archive. If you need to package additional libraries that have been downloaded by `scalajs-bundler`, you can do something like: ~~~ scala src="../../../sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets-cookbook/build.sbt#additional-files" ~~~ Also, any static resources that you would like to have in the resulting archive (i.e. `index.html`), should live inside the `src/universal` directory of your project. ================================================ FILE: manual/src/ornate/getting-started.md ================================================ # Getting started ## Basic setup You need to have `npm` installed on your system. Add the `sbt-scalajs-bundler` plugin to your Scala.js project, in your `project/plugins.sbt` file: ~~~ scala expandVars=true // For Scala.js 1.x addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "{{version}}") // Or, for Scala.js 0.6.x addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler-sjs06" % "{{version}}") ~~~ > {.note} > The plugin requires Scala.js 0.6.26+ or 1.0.0-RC1 and either > sbt 0.13.17+ or 1.2.1+. Enable the `ScalaJSBundlerPlugin`, in your `build.sbt` file: ~~~ scala enablePlugins(ScalaJSBundlerPlugin) ~~~ If you have a `main` method, make sure that you enable the Scala.js main module initializer with the following setting: ~~~ scala scalaJSUseMainModuleInitializer := true ~~~ Add dependencies to the npm packages your application requires, in your `build.sbt` file, e.g.: ~~~ scala npmDependencies in Compile += "snabbdom" -> "0.5.3" ~~~ > {.note} > You will most probably want to write a [Scala.js facade](https://www.scala-js.org/doc/interoperability/facade-types.html#-imports-from-other-javascript-modules) > for the JavaScript module. You can find information on how to do that in the > [cookbook](cookbook.md#facade), or draw inspiration from > [this example](https://github.com/scalacenter/scalajs-bundler/blob/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/browserless/src/main/scala/uuid/uuid.scala). Then, use the `fastOptJS::webpack` sbt command to download the npm packages and bundle your Scala.js application and its dependencies into a single JavaScript file executable by a web browser. In this example, the `webpack` sbt task produces a single file located at `target/scala-2.12/scalajs-bundler/main/-fastopt-bundle.js`. See complete examples in the [tests](https://github.com/scalacenter/scalajs-bundler/tree/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler). ## Integrating with sbt-web {#sbt-web} For sbt-web integration use the `sbt-web-scalajs-bundler` plugin instead of `sbt-scalajs-bundler`: ~~~ scala expandVars=true // For Scala.js 1.x addSbtPlugin("ch.epfl.scala" % "sbt-web-scalajs-bundler" % "{{version}}") // Or, for Scala.js 0.6.x addSbtPlugin("ch.epfl.scala" % "sbt-web-scalajs-bundler-sjs06" % "{{version}}") ~~~ Then, enable the `WebScalaJSBundlerPlugin` on the project that uses sbt-web: ~~~ scala lazy val server = project .settings( scalaJSProjects := Seq(client), pipelineStages in Assets := Seq(scalaJSPipeline) ) .enablePlugins(WebScalaJSBundlerPlugin) lazy val client = project.enablePlugins(ScalaJSBundlerPlugin) ~~~ You also need to setup the `ScalaJSBundlerPlugin` on the Scala.js project, as described in the preceding section, and the `sbt-web-scalajs` plugins as described in [their documentation](https://github.com/vmunier/sbt-web-scalajs). Note that `sbt-web-scalajs`'s `ScalaJSWeb` plugin must **not** be enabled, because `ScalaJSWeb` will create source mappings to source files copied to a hash path, which conflict with `ScalaJSBundlerPlugin`'s webpack-based source mappings. The `WebScalaJSBundlerPlugin` plugin automatically configures the `scalaJSPipeline` task to use the bundles rather than the output of the Scala.js compilation. You can see a complete example [here](https://github.com/scalacenter/scalajs-bundler/tree/ma/sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play). ================================================ FILE: manual/src/ornate/index.md ================================================ # scalajs-bundler scalajs-bundler is a module bundler for Scala.js projects that use npm packages: it bundles the .js file emitted by the Scala.js compiler with its npm dependencies into a single .js file executable by Web browsers. scalajs-bundler uses [npm](https://www.npmjs.com) and [webpack](https://webpack.github.io/) under the hood. Last stable version is ![](config:version): ~~~ scala expandVars=true // For Scala.js 1.x addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "{{version}}") // Or, for Scala.js 0.6.x addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler-sjs06" % "{{version}}") ~~~ If you're using [sbt-crossproject](https://github.com/portable-scala/sbt-crossproject) you need to add plugin via `jsConfigure`: ~~~ scala expandVars=true lazy val cross = crossProject(JSPlatform, JVMPlatform).in(file(".")) .jvmSettings(BuildSettings.jvmSettings) .jsSettings(BuildSettings.jsSettings) .jsConfigure { project => project.enablePlugins(ScalaJSBundlerPlugin) } ~~~ See the [**getting started**](getting-started.md) page for more details about the setup process. ================================================ FILE: manual/src/ornate/motivation.md ================================================ # Motivation The most popular JavaScript package registry is npm. Scala.js projects are usually based on sbt, which has no knowledge of npm. So, how can Scala.js developers get an *integrated experience* when they use libraries published on npm? ## WebJars A first solution is to use [WebJars](http://www.webjars.org/). WebJars wrap .js files into .jar archives published on maven central, so that Scala developers can depend on them just like they depend on JVM libraries. However, WebJars have limitations. First, npm packages are not automatically available as WebJars as soon as they are published on the npm registry. So, WebJars users have to explicitly publish these npm packages as .jar packages. This is just a matter of filling and submitting a form with the name and version of the package, but, still, this is one extra step. Second, the tool that re-publishes npm packages as .jar packages does not keep track of [transitive dependencies](https://github.com/webjars/webjars/issues/1186). It means that when your program depends on a library `foo` that itself depends on a library `bar`, then you have to explicitly convert both and depend on both in your program. This situation might turn into a dependency management hell. ## Double build Another solution consists in having two build systems: one for the Scala world and one for the npm world. At some point, the npm build writes files consumed by the Scala application. Typically, the npm build defines npm dependencies and bundles them into a single .js file suitable for web browser consumption and that the Scala.js program can depend on (e.g. using `jsDependencies`). This approach works well and allows developers to use whatever tools they want to process the npm dependencies. However, having two build systems is not really an _integrated_ developer experience: developers have to setup two builds, run two shells, and manually take care of the relationship between the two builds. ## scalajs-bundler scalajs-bundler aims to provide an integrated solution to work with npm packages from Scala.js projects. It lets developers define their npm dependencies from within their sbt build, fetches them (using npm itself), and bundles them with the output of the Scala.js compilation. The result is a single .js file suitable for web browser consumption. ================================================ FILE: manual/src/ornate/reference.md ================================================ # Reference ## `ScalaJSBundlerPlugin` The `ScalaJSBundlerPlugin` sbt plugin automatically enables `ScalaJSPlugin` on the project. It configures the kind of output of the project to be `ModuleKind.CommonJSModule`. Finally, it also configures its execution environment so that npm packages are fetched (by running the `npm update` command in the project’s target directory) before the project is `run` or `test`ed. It is also possible to bundle the application and its dependencies into a single .js file by using the `webpack` task scoped to a Scala.js stage (`fastOptJS` or `fullOptJS`): ~~~ > fastOptJS::webpack ~~~ The `webpack` task returns a list of artifacts produced by the bundling process. ### JavaScript Dependencies {#npm-dependencies} To define the npm packages your project depends on, use the `npmDependencies` key: ~~~ scala npmDependencies in Compile += "uuid" -> "~3.1.0" ~~~ You can also scope dependencies to `Test`: ~~~ scala npmDependencies in Test += "jasmine" -> "2.5.2" ~~~ > {.note} > Your facades must use > [`@JSImport`](https://www.scala-js.org/doc/interoperability/facade-types.html#a-nameimporta-imports-from-other-javascript-modules) > in order to work with the npm modules, otherwise you will need some additional configuration, as explained > [here](cookbook.md#global-namespace). Last but not least, the `.js` files that are in your classpath and in the `jsSourceDirectories` are automatically copied to the working directory of the `node` command. This means that you can also `@JSImport` these modules from your Scala facades (you can see an example [here](https://github.com/scalacenter/scalajs-bundler/blob/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/js-resources/src/main/scala/example/MyModule.scala#L6)). ### jsdom Support for Tests {#jsdom} If your tests execution environment require the DOM, add the following line to your build: ~~~ scala requireJsDomEnv in Test := true ~~~ Then, `ScalaJSBundlerPlugin` will automatically download jsdom and bundle the tests before their execution so that they can be loaded by jsdom. You can find an example of project requiring the DOM for its tests [here](https://github.com/scalacenter/scalajs-bundler/blob/main/sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/). ### Yarn {#yarn} By default, `npm` is used to fetch the dependencies but you can use [Yarn](https://yarnpkg.com/) by setting the `useYarn` key to `true`: ~~~ scala useYarn := true ~~~ If your sbt (sub-)project directory contains a `yarn.lock`, it will be used. Else, a new one will be created. You should check `yarn.lock` into source control. Yarn 0.22.0+ must be available on your machine. ### Bundling Mode {#bundling-mode} Each time you change something in your application source code and compile the project, Scala.js emits a new .js file that can weigh several MBs if your application is large. Scalajs-bundler provides a few different options with respect to handling this large output file, controlled by setting the optional `webpackBundlingMode` key and can be scoped to a Scala.js stage (`fastOptJS` or `fullOptJS`). #### Application (default) {#bundling-mode-application} `webpackBundlingMode := BundlingMode.Application` generates a webpack config that simply processes the Scala.js output file as an entrypoint. This means that webpack loaders, plugins, etc will work as usual. It also means that webpack will have to process a very large Scala.Js output file. Turning this CommonJS module into code executable by web browsers takes time, often upwards of a minute. Nonetheless, this is identical to what you'd get if were to duplicate your workflow outside scalajs-bundler, so it remains the default. #### Library Only {#bundling-mode-library-only} You can get a much faster “change source and reload application” workflow by setting the `webpackBundlingMode := BundlingMode.LibraryOnly()` key. This bundling mode avoids having webpack process the entire Scala.js output, but instead uses webpack to bundle all the javascript dependencies (determined via `@JSImport` and any changes to the `webpack.config.js`). This is accomplished by setting the webpack `output.library` and `output.libraryTarget` keys in the webpack.config. By default, this generates a global variable named `ScalaJSBundlerLibrary`, but if needed, that variable name can be overridden via the `exportedName` parameter provided to `BundlingMode.LibraryOnly`. The drawback of this mode is that because the output of Scala.js is not processed by Webpack, it is still a CommonJS module that is not directly executable by web browsers. This problem is addressed by including a special “loader” before including the Scala.js module. > {.warning} > The inclusion of a CommonJS module in the web browser is not guaranteed to work. In particular, > in `fullOptJS` mode the name mangler might produce names that could clash with other existing > global names. For this reason, we don’t recommend using the `LibraryOnly` bundling mode in `fullOptJS`, > or in production. In order to use this mode, instead of including `yourapp-bundle.js` in your page, you will need to include `yourapp-library.js` (which contains all the application libraries bundled into a single file), `yourapp-loader.js` (which contains a hack to make the application work), and `yourapp-fastopt.js` or `yourapp-opt.js` (which contains the output of Scala.js, that is a CommonJS module). If you're using Play Framework, you could use a twirl template similar to this one: ~~~ html @(projectName: String, assets: String => String, resourceExists: String => Boolean, htmlAttributes: Html = Html("") ) @defining(s"${projectName.toLowerCase}") { name => @Seq(s"$name-opt-library.js", s"$name-fastopt-library.js").find(resourceExists).map(name => jsScript(assets(name), htmlAttributes)) } @defining(s"${projectName.toLowerCase}") { name => @Seq(s"$name-opt.js", s"$name-fastopt.js").find(resourceExists).map(name => jsScript(assets(name), htmlAttributes)) } ~~~ The default variable global for the library is `ScalaJsBundlerDependencies`. Should you need to change it, you can provide a new variable name to the configuration, such as `webpackBundlingMode := BundlingMode.LibrariesOnly("myLib")`. ##### Benefits By avoiding processing the entire Scala.js output file, webpack times from 10's of seconds to minutes to a couple of seconds, depending on how many dependencies webpack has to resolve. In addition, since the module references are all still managed by webpack, any custom loaders such as the `html-loader` or `text-loader` will work as usual. Other webpack configuration such as external modules also work out of the box. By splitting the output into a separate library and Scala.js app we are able to leverage browser caching for most reloads, since it's seldom that both .js assets and Scala.js assets change at the same time. By avoiding any post-processing of the Scala.js output, we leave the sourcemap generated by the Scala.js compiler entirely intact. This means we avoid any translation or lookup errors introduced by webpack, and also means we save a bunch more processing time even if sourcemaps are enabled. ##### How It Works Scalajs-bundler has a unique advantage in the JavaScript build ecosystem in that it runs inside SBT and has access to the Scala.Js compiler internals. By leveraging these, we can generate a fake `entrypoint.js` file that represents all the `@JSImport`s from a Scala.JS project. We can use this entrypoint to generate a library file that provides access to all those imports without webpack having to ever process the entire Scala.js output. Inside the `entrypoint.js` file we also provide a trivial `require` implementation that provides access to the modules which were imported. All that remains is to provide an `exports` variable with a reference to the `require` implementation for the Scala.js out to hang onto, which is what the loader provides. #### Library and Application {#bundling-mode-library-and-application} `bundlingMode := BundlingMode.LibraryAndApplication()` builds on `BundlingMode.LibraryOnly` and attempts to produce only one artifact (like the `Application` bundling mode) without the overhead processing the entire Scala.js output file. It uses the same library file generation process as `BundlingMode.LibraryOnly`. It then bundles that library, the loader, and the Scala.js output into a `yourapp-bundle` file by concatenating them. If `enableSourceMaps := true`, it will attempt to use the node.js `concat-with-sourcemaps` module to combine the sourcemaps as well. If you need interoperability with the full `Application` mode or somehow can't handle the two files generated by the `LibraryOnly()` mode, this mode may be useful for you. However, it relies on post-processing and eliminates some of the benefits of the `LibraryOnly` mode, so consider carefully if you really need it before turning it on. ### Tasks and Settings {#tasks-and-settings} The tasks and settings that control the plugin are documented in the API documentation of the [ScalaJSBundlerPlugin](api:scalajsbundler.sbtplugin.ScalaJSBundlerPlugin$). ## `WebScalaJSBundlerPlugin` The `WebScalaJSBundlerPlugin` provides integration with [sbt-web-scalajs](https://github.com/vmunier/sbt-web-scalajs). Enable this plugin on JVM projects that need to use .js artifacts produced by Scala.js projects (ie in places where you used to enable `WebScalaJS`). The plugin tunes the `scalaJSPipeline` to use the bundles produced by webpack rather than the direct output of the Scala.js compilation. ### Importing Assets from NPM Packages {#npm-assets} Some NPM packages also contain static assets (e.g. fonts, stylesheets, images, etc.). You can make them available as sbt-web assets as follows: ~~~ scala npmAssets ++= NpmAssets.ofProject(client) { nodeModules => (nodeModules / "font-awesome").allPaths // sbt 1.0.0+ }.value ~~~ > Note: for older sbt versions use `(nodeModules / "font-awesome").***` instead. Where `client` is the identifier of an sbt project that uses the `ScalaJSBundlerPlugin`. The above configuration makes all the files within the `font-awesome` package available as sbt-web assets. These assets keep their path prefix relative to the `node_modules` directory: for instance the asset path of the `css/font-awesome.min.css` resource is `font-awesome/css/font-awesome.min.css`. ### Tasks and Settings {#web-tasks-and-settings} The tasks and settings that control the plugin are documented in the API documentation of the [WebScalaJSBundlerPlugin](api:scalajsbundler.sbtplugin.WebScalaJSBundlerPlugin$). ================================================ FILE: manual/src/ornate.conf ================================================ global { toc = [ { title = "Introduction", url = "index.md" } motivation.md getting-started.md reference.md cookbook.md community.md changelog.md ] } meta { siteTitle = "scalajs-bundler" } version = ${version} extension.scaladocLinks { api { index = "unchecked:/api/latest/index.html" } } ================================================ FILE: project/build.properties ================================================ sbt.version = 1.8.2 ================================================ FILE: project/plugins.sbt ================================================ addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.4.1") addSbtPlugin("com.novocode" % "sbt-ornate" % "0.6") addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0") addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/main/scala/scalajsbundler/BundlerFile.scala ================================================ package scalajsbundler import java.io.File import java.nio.file.Path import scalajsbundler.Stats.WebpackStats import scalajsbundler.util.CachedBundleFiles import scala.collection.immutable.ListSet /** * Files used in the `ScalaJSBundler` pipeline. */ sealed trait BundlerFile extends Product with Serializable { def file: File } object BundlerFile { /** * Files that may be inputs to the webpack process */ sealed trait WebpackInput extends BundlerFile { def project: String } /** * Internal-only files */ sealed abstract class Internal extends BundlerFile /** * A library-mode entrypoint file * @param application The [[Application]] this entrypoint was generated from * @param file The file containing the entry point */ case class EntryPoint(application: Application, file: java.io.File) extends Internal with WebpackInput { def project: String = application.project } object EntryPoint { /** Filename of the generated bundle, given its module entry name */ def fileName(entry: String): String = s"$entry-entrypoint.js" } /** * The package.json file, used for populating the node_modules folder * * @param file The file reference for the package.json */ case class PackageJson(file: java.io.File) extends Internal /** * A webpack configuration file. * * @param application The [[Application]] this file runs webpack for * @param file The webpack.config.js file reference */ case class WebpackConfig(application: Application, file: java.io.File) extends Internal { def project: String = application.project def targetDir: Path = file.getParentFile.toPath /** * Returns the Library identifying the asset produced by scala.js through webpack stats */ def asLibrary(stats: Option[WebpackStats]): Library = Library(project, stats.flatMap { s => s.resolveAsset(targetDir, project) }.getOrElse(targetDir.resolve(Library.fileName(project)).toFile), stats.map { s => s.resolveAllAssets(targetDir) }.getOrElse(Nil) ) /** * Returns library from a set of cached files * By convention the first element is the `file` and the rest are the assets */ def asLibraryFromCached(cached: Set[File]): Library = { assert(cached.size >= 1) val assets = if (cached.size == 1) Nil else cached.tail.toList Library(project, cached.head, assets) } /** * Returns the Application for this configuration identifying the asset produced by scala.js through webpack stats */ def asApplicationBundle(stats: Option[WebpackStats]): ApplicationBundle = ApplicationBundle(project, stats.flatMap { s => s.resolveAsset(targetDir, project) }.getOrElse(targetDir.resolve(ApplicationBundle.fileName(project)).toFile), stats.map { s => s.resolveAllAssets(targetDir) }.getOrElse(Nil)) /** * Returns an application bundle from a set of cached files * By convention the first element is the `file` and the rest are the assets */ def asApplicationBundleFromCached(cached: Set[File]): ApplicationBundle = { assert(cached.size >= 1) val assets = if (cached.size == 1) Nil else cached.tail.toList ApplicationBundle(project, cached.head, assets) } } /** * Public webpack artifacts -- those that might be served to clients or packaged */ sealed abstract class Public extends BundlerFile { def project: String // Attributed files, the first is the main file and the rest are assets def attributedFiles: (File, Seq[File]) = (file, Seq.empty) def `type`: BundlerFileType } /** * The Scala.js application itself, aka -fastopt.js or -opt.js * * @param project The application project name * @param file The file containing the application javascript * @param assets All the assets on the application */ case class Application(project: String, file: File, assets: List[java.io.File]) extends Public with WebpackInput { def targetDir: Path = file.getParentFile.toPath val `type`: BundlerFileType = BundlerFileType.Application def asLoader: Loader = Loader(this, targetDir .resolve(Loader.fileName(project)) .toFile) def asEntryPoint: EntryPoint = EntryPoint(this, targetDir .resolve(EntryPoint.fileName(project)) .toFile) def asApplicationBundle: ApplicationBundle = ApplicationBundle(project, targetDir .resolve(ApplicationBundle.fileName(project)) .toFile, assets) /** * Returns an application bundle from a set of cached files */ def asApplicationBundleFromCached(cached: Set[File]): ApplicationBundle = { assert(cached.size >= 1) val assets = if (cached.size == 1) Nil else cached.tail.toList ApplicationBundle(project, cached.head, assets) } } /** * A webpack library bundle, containing only libraries * @param project The project the library bundle was generated for * @param file The file containing the application javascript * @param assets All the assets on the application */ case class Library(project: String, file: File, assets: List[java.io.File]) extends Public { val `type`: BundlerFileType = BundlerFileType.Library val cached: ListSet[File] = CachedBundleFiles.cached(file, assets) override def attributedFiles: (File, Seq[File]) = (file, assets) } object Library { /** Suffix to apply to the libraries bundle */ val suffix: String = "-library.js" /** Filename of the generated libraries bundle, given its module entry name */ def fileName(entry: String): String = s"$entry$suffix" } /** * A webpack loader file. Allows an [[Application]] to access the dependencies bundled * into a [[Library]] * @param application Application to be loaded * @param file Loader file */ case class Loader(application: Application, file: java.io.File) extends Public { val `type`: BundlerFileType = BundlerFileType.Loader def project: String = application.project } object Loader { /** Suffix to apply to the loaders file */ val suffix: String = "-loader.js" /** Filename of the generated bundle, given its module entry name */ def fileName(entry: String): String = s"$entry$suffix" } /** * A fully self-contained application bundle, including all dependencies. * * @param project The project name * @param file The file containing the application javascript * @param assets All the assets on the application */ case class ApplicationBundle(project: String, file: File, assets: List[java.io.File]) extends Public { val `type`: BundlerFileType = BundlerFileType.ApplicationBundle val cached: ListSet[File] = CachedBundleFiles.cached(file, assets) override def attributedFiles: (File, Seq[File]) = (file, assets) } object ApplicationBundle { /** Filename of the generated bundle, given its module entry name */ def fileName(entry: String): String = s"$entry-bundle.js" } } ================================================ FILE: sbt-scalajs-bundler/src/main/scala/scalajsbundler/BundlerFileType.scala ================================================ package scalajsbundler /** * The type of a given [[BundlerFile.Public]]. Used for tagging files when they are produced by * the ScalaJsBundler and handed off to other SBT tasks. */ sealed abstract class BundlerFileType object BundlerFileType { /** * Scala.js application */ case object Application extends BundlerFileType /** * Library dependencies provided by webpack */ case object Library extends BundlerFileType /** * Dependency loader, provides [[Library]] dependencies to [[Application]] */ case object Loader extends BundlerFileType /** * Fully linked application bundle, containing [[Application]] and all it's dependencies */ case object ApplicationBundle extends BundlerFileType /** * An asset of the bundled application */ case object Asset extends BundlerFileType } ================================================ FILE: sbt-scalajs-bundler/src/main/scala/scalajsbundler/BundlingMode.scala ================================================ package scalajsbundler /** * The BundlingMode dictates how webpack is configured for bundling. Please refer * to the members of the sealed family details. */ sealed trait BundlingMode object BundlingMode { /** * Bundle the entire application with webpack. Using this mode, the webpack `entry` key will * contain the Scala.js output file. All dependencies will be resolved via webpack, and the entire * bundle will be processed by any plugins. The output will be a runnable bundle, not a library. * * To see the specific webpack configuration that will be generated, refer to [[Webpack.writeConfigFile]]. */ case object Application extends BundlingMode /** * Shared base class for [[LibraryOnly]] and [[LibraryAndApplication]]. Both * must provide an `exportedName` field indicating what javascript global * the libraries will be exported to. Both library modes will generate an `entrypoints` file based * on the Scala.js imports and use that as the `entrypoint` for the generated `webpack.config.js`. The webpack * output will be a library, which will assign itself to a global variable when loaded. * * The `entrypoints` file also contains a `require` implementation, which can be exposed globally by including * the `loader` file. Refer to [[util.JSBundler.loaderScript]] for an example of such a script. * * To see the specific webpack configuration that will be generated, refer to [[Webpack.writeConfigFile]]. */ sealed trait Library extends BundlingMode { /** * Name of the global variable containing the dependencies */ def exportedName: String } /** * Bundle only the libraries used by the application. This mode will generate an `entrypoints` file based * on the Scala.js imports and use that as the entrypoint for the generated `webpack.config.js`. The webpack * output will be a library, which will assign itself to a global variable when loaded. * * The `library` file produce in this mode must be combined with the `loader` and the Scala.js output in order * to fully duplicate the usability of [[Application]] mode. * * Refer to [[Library]] for additional details. * */ case class LibraryOnly(exportedName: String = defaultLibraryExportedName) extends Library /** * Builds on [[LibraryOnly]] by generating the `loader` and concatenating it with the Scala.js output file. This * output is designed to be a drop-in replacement for fully processing the file via webpack ([[Application]] mode). When * `webpackEmitSourceMaps := true`, this mode will attempt to merge all the files using the node.js * 'concat-with-sourcemaps' module. * * Refer to [[Library]] for additional details. */ case class LibraryAndApplication(exportedName: String = defaultLibraryExportedName) extends Library /** * The default exported library name, used by [[LibraryOnly]] and [[LibraryAndApplication]] */ val defaultLibraryExportedName = "ScalaJSBundlerLibrary" /** * The default BundlingMode used by the ScalaJSBundler */ val Default: BundlingMode = Application } ================================================ FILE: sbt-scalajs-bundler/src/main/scala/scalajsbundler/ExternalCommand.scala ================================================ package scalajsbundler import java.io.File import sbt._ import scalajsbundler.util.Commands /** * Attempts to smoothen platform-specific differences when invoking commands. * * @param name Name of the command to run */ class ExternalCommand(name: String) { /** * Runs the command `cmd` * @param args Command arguments * @param workingDir Working directory of the process * @param logger Logger */ def run(args: String*)(workingDir: File, logger: Logger): Unit = Commands.run(cmd ++: args, workingDir, logger) private val cmd = sys.props("os.name").toLowerCase match { case os if os.contains("win") => Seq("cmd", "/c", name) case _ => Seq(name) } } object Npm extends ExternalCommand("npm") object Yarn extends ExternalCommand("yarn") object ExternalCommand { private val yarnOptions = List("--non-interactive", "--mutex", "network") private def syncLockfile( lockFileName: String, baseDir: File, installDir: File, logger: Logger )( command: => Unit ): Unit = { val sourceLockFile = baseDir / lockFileName val targetLockFile = installDir / lockFileName if (sourceLockFile.exists()) { logger.info("Using lockfile " + sourceLockFile) IO.copyFile(sourceLockFile, targetLockFile) } command if (targetLockFile.exists()) { logger.debug("Wrote lockfile to " + sourceLockFile) IO.copyFile(targetLockFile, sourceLockFile) } } private def syncYarnLockfile( baseDir: File, installDir: File, logger: Logger )( command: => Unit ): Unit = { syncLockfile("yarn.lock", baseDir, installDir, logger)(command) } private def syncNpmLockfile( baseDir: File, installDir: File, logger: Logger )( command: => Unit ): Unit = { syncLockfile("package-lock.json", baseDir, installDir, logger)(command) } /** * Locally install NPM packages * * @param baseDir The (sub-)project directory which contains yarn.lock * @param installDir The directory in which to install the packages * @param useYarn Whether to use yarn or npm * @param logger sbt logger * @param npmExtraArgs Additional arguments to pass to npm * @param npmPackages Packages to install (e.g. "webpack", "webpack@2.2.1") */ def addPackages(baseDir: File, installDir: File, useYarn: Boolean, logger: Logger, npmExtraArgs: Seq[String], yarnExtraArgs: Seq[String])(npmPackages: String*): Unit = if (useYarn) { syncYarnLockfile(baseDir, installDir, logger) { Yarn.run("add" +: (yarnOptions ++ yarnExtraArgs ++ npmPackages): _*)( installDir, logger) } } else { syncNpmLockfile(baseDir, installDir, logger) { Npm.run("install" +: (npmPackages ++ npmExtraArgs): _*)(installDir, logger) } } def install(baseDir: File, installDir: File, useYarn: Boolean, logger: Logger, npmExtraArgs: Seq[String], yarnExtraArgs: Seq[String]): Unit = if (useYarn) { syncYarnLockfile(baseDir, installDir, logger) { Yarn.run("install" +: (yarnOptions ++ yarnExtraArgs): _*)(installDir, logger) } } else { syncNpmLockfile(baseDir, installDir, logger) { Npm.run("install" +: npmExtraArgs: _*)(installDir, logger) } } } ================================================ FILE: sbt-scalajs-bundler/src/main/scala/scalajsbundler/JSDOMNodeJSEnv.scala ================================================ package scalajsbundler import scala.util.control.NonFatal import sbt._ import java.io._ import java.nio.charset.StandardCharsets import java.nio.file.{Files, Path, StandardCopyOption} import java.net.URI import org.scalajs.jsenv._ import org.scalajs.jsenv.nodejs._ import org.scalajs.jsenv.JSUtils.escapeJS // HACK Copy of Scala.js’ JSDOMNodeJSEnv. The only change is the ability to pass the directory in which jsdom has been installed class JSDOMNodeJSEnv(config: JSDOMNodeJSEnv.Config) extends JSEnv { val name: String = "Node.js with JSDOM" def start(input: Seq[Input], runConfig: RunConfig): JSRun = { JSDOMNodeJSEnv.validator.validate(runConfig) val scripts = validateInput(input) try { internalStart(codeWithJSDOMContext(scripts), runConfig) } catch { case NonFatal(t) => JSRun.failed(t) } } def startWithCom(input: Seq[Input], runConfig: RunConfig, onMessage: String => Unit): JSComRun = { JSDOMNodeJSEnv.validator.validate(runConfig) val scripts = validateInput(input) ComRun.start(runConfig, onMessage) { comLoader => internalStart(comLoader :: codeWithJSDOMContext(scripts), runConfig) } } private def validateInput(input: Seq[Input]): List[Path] = { input.map { case Input.Script(script) => script case _ => throw new UnsupportedInputException(input) }.toList } private def internalStart(files: List[Path], runConfig: RunConfig): JSRun = { val command = config.executable :: config.args val externalConfig = ExternalJSRun.Config() .withEnv(env) .withRunConfig(runConfig) ExternalJSRun.start(command, externalConfig)(JSDOMNodeJSEnv.write(files)) } private def env: Map[String, String] = Map("NODE_MODULE_CONTEXTS" -> "0") ++ config.env private def codeWithJSDOMContext(scripts: List[Path]): List[Path] = { val scriptsURIs = scripts.map(JSDOMNodeJSEnv.materialize(_)) val scriptsURIsAsJSStrings = scriptsURIs.map(uri => "\"" + escapeJS(uri.toASCIIString) + "\"") val scriptsURIsJSArray = scriptsURIsAsJSStrings.mkString("[", ", ", "]") val jsDOMCode = { s""" | |(function () { | var jsdom = require("jsdom"); | | if (typeof jsdom.JSDOM === "function") { | // jsdom >= 10.0.0 | var virtualConsole = new jsdom.VirtualConsole() | .sendTo(console, { omitJSDOMErrors: true }); | virtualConsole.on("jsdomError", function (error) { | try { | // Display as much info about the error as possible | if (error.detail && error.detail.stack) { | console.error("" + error.detail); | console.error(error.detail.stack); | } else { | console.error(error); | } | } finally { | // Whatever happens, kill the process so that the run fails | process.exit(1); | } | }); | | var dom = new jsdom.JSDOM("", { | virtualConsole: virtualConsole, | url: "http://localhost/", | | /* Allow unrestricted ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/library/project/plugins.sbt ================================================ val scalaJSVersion = sys.props.getOrElse("scalajs.version", sys.error("'scalajs.version' environment variable is not defined")) val scalaJSBundlerVersion = sys.props.getOrElse("plugin.version", sys.error("'plugin.version' environment variable is not set")) addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % scalaJSBundlerVersion) libraryDependencies += "net.sourceforge.htmlunit" % "htmlunit" % "2.46.0" ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/library/src/main/scala/example/Library.scala ================================================ //#library-definition package example import scala.scalajs.js.annotation.{JSExportTopLevel, JSExportAll} @JSExportTopLevel(name="sjs_example_Library") @JSExportAll object Library { def foo(): String = SomeOtherCode.quux(true) def bar(): String = SomeOtherCode.quux(false) } //#library-definition object SomeOtherCode { import uuid.UUID def quux(b: Boolean): String = if (b) UUID.v4() else UUID.v1() } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/library/src/main/scala/uuid/uuid.scala ================================================ package uuid import scala.annotation.meta.field import scala.scalajs.js import scala.scalajs.js.annotation.JSImport.Namespace import scala.scalajs.js.annotation.{JSExport, JSImport} import scala.scalajs.js.| @JSImport("uuid", Namespace) @js.native object UUID extends js.Object { def v1(options: js.UndefOr[UUIDOptions] = js.undefined): String = js.native def v1( options: UUIDOptions, buffer: js.Array[Double], offset: js.UndefOr[Double] ): js.Array[Double] = js.native def v4(options: js.UndefOr[UUIDOptions] = js.undefined): String = js.native def v4( options: UUIDOptions, buffer: js.Array[Double], offset: js.UndefOr[Double] ): js.Array[Double] = js.native def parse( id: String, buffer: js.UndefOr[js.Array[Double]] = js.undefined, offset: js.UndefOr[Double] = js.undefined ): js.Array[Double] = js.native def unparse( buffer: js.Array[Double], offset: js.UndefOr[Double] = js.undefined ): String = js.native } case class UUIDOptions( @(JSExport @field) node: js.UndefOr[js.Array[js.Any]] = js.undefined, @(JSExport @field) clockseq: js.UndefOr[Double] = js.undefined, @(JSExport @field) msecs: js.UndefOr[Double | js.Date] = js.undefined, @(JSExport @field) nsecs: js.UndefOr[Double] = js.undefined ) ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/library/test ================================================ > fullOptJS::webpack > html index.html ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/newer-linker/build.sbt ================================================ name := "newer-linker" enablePlugins(ScalaJSBundlerPlugin, ScalaJSJUnitPlugin) scalaVersion := "2.13.1" scalaJSUseMainModuleInitializer := true // Adds a dependency on the uuid npm package npmDependencies in Compile += "uuid" -> "3.1.0" ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/newer-linker/project/newer-scala-js.sbt ================================================ // TODO Set this to a version > 1.3.0 when there is one addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.3.0") ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/newer-linker/project/plugins.sbt ================================================ val scalaJSVersion = sys.props.getOrElse("scalajs.version", sys.error("'scalajs.version' environment variable is not defined")) val scalaJSBundlerVersion = sys.props.getOrElse("plugin.version", sys.error("'plugin.version' environment variable is not set")) addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % scalaJSBundlerVersion) ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/newer-linker/src/test/scala/example/NewerLinkerTest.scala ================================================ package example import org.junit.Assert._ import org.junit.Test class NewerLinkerTest { @Test def newerLinker(): Unit = { /* TODO Set this to a version > 1.3.0 when there is one, and adapt the * test below to something that would have been fixed in the meantime. */ assertEquals("1.3.0", System.getProperty("java.vm.version")) /* Test the fix to https://github.com/scala-js/scala-js/issues/3984, which * was shipped in Scala.js 1.0.1. */ def minusZero: Any = -0.0f assertSame(classOf[java.lang.Float], minusZero.getClass()) } } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/newer-linker/test ================================================ > test ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sbt-1.8/build.sbt ================================================ name := "newer-linker" enablePlugins(ScalaJSBundlerPlugin, ScalaJSJUnitPlugin) scalaVersion := "2.13.1" scalaJSUseMainModuleInitializer := true // Adds a dependency on the uuid npm package npmDependencies in Compile += "uuid" -> "3.1.0" ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sbt-1.8/project/build.properties ================================================ sbt.version=1.8.2 ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sbt-1.8/project/plugins.sbt ================================================ val scalaJSVersion = sys.props.getOrElse("scalajs.version", sys.error("'scalajs.version' environment variable is not defined")) val scalaJSBundlerVersion = sys.props.getOrElse("plugin.version", sys.error("'plugin.version' environment variable is not set")) addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % scalaJSBundlerVersion) ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sbt-1.8/src/test/scala/example/BasicTest.scala ================================================ package example import org.junit.Assert._ import org.junit.Test class BasicTest { @Test def newerLinker(): Unit = { assertEquals("1", 1.0.toString()) } } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sbt-1.8/test ================================================ > test ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig/README.md ================================================ scalajs-bundler/sharedconfig ===================== An application that uses npm packages and that produces a static HTML page containing a leaflet map Demonstrates how to: - depend on npm packages ; - using custom webpack loader configuration ; - use a shared webpack configuration ; - differentiate prod/dev builds. ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig/build.sbt ================================================ name := "sharedconfig" enablePlugins(ScalaJSBundlerPlugin, ScalaJSJUnitPlugin) scalaVersion := "2.11.12" scalaJSUseMainModuleInitializer := true libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "1.0.0" npmDependencies in Compile += "leaflet" -> "0.7.7" npmDevDependencies in Compile ++= Seq( "webpack-merge" -> "4.1.2", "file-loader" -> "6.2.0", "image-webpack-loader" -> "7.0.1", "css-loader" -> "5.0.1", "style-loader" -> "2.0.0", "url-loader" -> "4.1.0" ) webpackConfigFile in fastOptJS := Some(baseDirectory.value / "dev.webpack.config.js") // Use a different Webpack configuration file for production webpackConfigFile in fullOptJS := Some(baseDirectory.value / "prod.webpack.config.js") // Use the shared Webpack configuration file for reload workflow and for running the tests webpackConfigFile in Test := Some(baseDirectory.value / "common.webpack.config.js") // Execute the tests in browser-like environment requireJsDomEnv in Test := true webpackBundlingMode in fastOptJS := BundlingMode.LibraryAndApplication() useYarn := true // HtmlUnit does not support ECMAScript 2015 scalaJSLinkerConfig ~= { _.withESFeatures(_.withUseECMAScript2015(false)) } // Check that a HTML can be loaded (and that its JavaScript can be executed) without errors InputKey[Unit]("html") := { import complete.DefaultParsers._ val page = (Space ~> StringBasic).parsed import com.gargoylesoftware.htmlunit.WebClient val client = new WebClient() try { client.getPage(s"file://${baseDirectory.value.absolutePath}/$page") } finally { client.close() } } TaskKey[Unit]("checkSize") := { val files = (webpack in (Compile, fullOptJS)).value val bundleFile = files .find(_.metadata.get(BundlerFileTypeAttr).exists(_ == BundlerFileType.ApplicationBundle)) .get.data val artifactSize = IO.readBytes(bundleFile).length // Account for minor variance in size due to transitive dependency updates assert(artifactSize > 150000 && artifactSize < 170000) } ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig/common.webpack.config.js ================================================ module.exports = { module: { rules: [ { test: /\.(jpe?g|png|gif|svg)$/i, use: [ { loader: "file-loader", options: { hash: "sha512", digest: "hex", name: "[hash].[ext]" } }, { loader: "image-webpack-loader", options: { bypassOnDebug: true, query: { mozjpeg: { progressive: true }, gifsicle: { interlaced: true }, optipng: { optimizationLevel: 7 } } } } ] }, { test: /\.css$/, use: ["style-loader", "css-loader"] }, { test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/, loader: "url-loader", options: { limit: 10000 } } ] } }; ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig/dev.webpack.config.js ================================================ var merge = require("webpack-merge"); var generatedConfig = require('./scalajs.webpack.config'); var commonConfig = require("./common.webpack.config.js"); module.exports = merge(generatedConfig, commonConfig); ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig/index-prod.html ================================================ Example
================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig/index.html ================================================ Example
================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig/prod.webpack.config.js ================================================ var merge = require("webpack-merge"); var generatedConfig = require("./scalajs.webpack.config"); var commonConfig = require("./common.webpack.config.js"); module.exports = merge(generatedConfig, commonConfig); ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig/project/plugins.sbt ================================================ val scalaJSVersion = sys.props.getOrElse("scalajs.version", sys.error("'scalajs.version' environment variable is not defined")) val scalaJSBundlerVersion = sys.props.getOrElse("plugin.version", sys.error("'plugin.version' environment variable is not set")) addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % scalaJSBundlerVersion) libraryDependencies += "net.sourceforge.htmlunit" % "htmlunit" % "2.46.0" ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig/src/main/scala/example/Main.scala ================================================ package example import scala.scalajs.js import leaflet.modules._ object Main { def main(args: Array[String]): Unit = { LeafletAssets val map = Leaflet.map("container").setView(js.Array(51.505f, -0.09f), 13) val tileLayer = Leaflet.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") map.addLayer(tileLayer) } } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig/src/main/scala/leaflet/modules/modules.scala ================================================ package leaflet package modules import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import scala.scalajs.js.annotation.JSImport.Namespace @js.native trait Map extends js.Object { def setView(center: js.Array[Float], zoom: Int): Map = js.native def getZoom(): Int = js.native def addLayer(layer: Layer): js.Dynamic = js.native } @js.native trait Layer extends js.Object { def addTo(map: Map): js.Dynamic = js.native } @JSImport("leaflet", JSImport.Namespace) @js.native object Leaflet extends js.Object { def map(elem: String): Map = js.native def tileLayer(url: String): Layer = js.native } @JSImport("!style-loader!css-loader!leaflet/dist/leaflet.css", JSImport.Default ) @js.native object LeafletAssets extends js.Object {} ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig/src/test/scala/example/SomeTest.scala ================================================ package example import org.junit.Assert._ import org.junit.Test import org.scalajs.dom.document import scala.scalajs.js import leaflet.modules._ class SomeTest { @Test def leafletZoom(): Unit = { val container = document.body.innerHTML = """
""" val map = Leaflet.map("container").setView(js.Array(51.505f, -0.09f), 13) val zoomlevel = map.getZoom() assertEquals(13, zoomlevel) } } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/sharedconfig/test ================================================ $ absent target/scala-2.11/scalajs-bundler/main/sharedconfig-fastopt-bundle.js target/scala-2.11/scalajs-bundler/main/sharedconfig-fastopt-bundle.js.map > fastOptJS::webpack $ exists target/scala-2.11/scalajs-bundler/main/sharedconfig-fastopt-bundle.js target/scala-2.11/scalajs-bundler/main/sharedconfig-fastopt-bundle.js.map > html index.html $ absent target/scala-2.11/scalajs-bundler/main/sharedconfig-opt-bundle.js target/scala-2.11/scalajs-bundler/main/sharedconfig-opt-bundle.js.map > fullOptJS::webpack $ exists target/scala-2.11/scalajs-bundler/main/sharedconfig-opt-bundle.js > html index-prod.html > checkSize > test ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/README.md ================================================ scalajs-bundler/static ===================== An application that uses npm packages and that produces a static HTML page. Demonstrates how to: - depend on npm packages. ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/build.sbt ================================================ name := "static" enablePlugins(ScalaJSBundlerPlugin, ScalaJSJUnitPlugin) scalaVersion := "2.11.12" scalaJSUseMainModuleInitializer := true libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "1.0.0" npmDependencies in Compile += "snabbdom" -> "0.5.3" // Execute the tests in browser-like environment requireJsDomEnv in Test := true version in installJsdom := "16.4.0" webpackBundlingMode := BundlingMode.LibraryAndApplication() useYarn := true // HtmlUnit does not support ECMAScript 2015 scalaJSLinkerConfig ~= { _.withESFeatures(_.withUseECMAScript2015(false)) } // Check that a HTML can be loaded (and that its JavaScript can be executed) without errors InputKey[Unit]("html") := { import complete.DefaultParsers._ val page = (Space ~> StringBasic).parsed import com.gargoylesoftware.htmlunit.WebClient val client = new WebClient() try { client.getPage(s"file://${baseDirectory.value.absolutePath}/$page") } finally { client.close() } } TaskKey[Unit]("checkSize") := { //#filter-files val files = (webpack in (Compile, fullOptJS)).value val bundleFile = files .find(_.metadata.get(BundlerFileTypeAttr).exists(_ == BundlerFileType.ApplicationBundle)) .get.data //#filter-files val artifactSize = IO.readBytes(bundleFile).length val sizeLow = 17000 val sizeHigh = 22000 // Account for minor variance in size due to transitive dependency updates assert( artifactSize >= sizeLow && artifactSize <= sizeHigh, s"expected: [$sizeLow, $sizeHigh], got: $artifactSize" ) } ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/index-prod.html ================================================ Example
================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/index.html ================================================ Example
================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/project/plugins.sbt ================================================ val scalaJSVersion = sys.props.getOrElse("scalajs.version", sys.error("'scalajs.version' environment variable is not defined")) val scalaJSBundlerVersion = sys.props.getOrElse("plugin.version", sys.error("'plugin.version' environment variable is not set")) addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % scalaJSBundlerVersion) libraryDependencies += "net.sourceforge.htmlunit" % "htmlunit" % "2.46.0" ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/src/main/scala/example/Main.scala ================================================ package example import scala.scalajs.js import scala.scalajs.js.Dynamic.literal import snabbdom.{snabbdom, h, modules} import org.scalajs.dom.document object Main { def main(args: Array[String]): Unit = { val someFn = () => println("someFn") val anotherEventHandler = () => println("another event handler") // Based on https://github.com/paldepind/snabbdom#inline-example val patch = snabbdom.init(js.Array( // Init patch function with choosen modules modules.`class`, // makes it easy to toggle classes modules.props, // for setting properties on DOM elements modules.style, // handles styling on elements with support for animations modules.eventlisteners // attaches event listeners )) val vnode = h("div#container.two.classes", literal(on = literal(click = someFn)), js.Array( h("span", literal(style = literal(fontWeight = "bold")), "This is bold": js.Any), " and this is just normal text", h("a", literal(props = literal(href = "/foo")), "I'll take you places!": js.Any) )) val container = document.getElementById("container") // Patch into empty DOM element – this modifies the DOM as a side effect patch(container, vnode) val newVnode = h("div#container.two.classes", literal(on = literal(click = anotherEventHandler)), js.Array( h("span", literal(style = literal(fontWeight = "normal", fontStyle = "italic")), "This is now italic type": js.Any), " and this is still just normal text", h("a", literal(props = literal(href = "/bar")), "I'll take you places!": js.Any) )) // Second `patch` invocation patch(vnode, newVnode) // Snabbdom efficiently updates the old view to the new state } } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/src/main/scala/snabbdom/modules/modules.scala ================================================ package snabbdom package modules import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import scala.scalajs.js.annotation.JSImport.Namespace @JSImport("snabbdom/modules/class", Namespace) @js.native object `class` extends js.Object @JSImport("snabbdom/modules/props", Namespace) @js.native object props extends js.Object @JSImport("snabbdom/modules/style", Namespace) @js.native object style extends js.Object @JSImport("snabbdom/modules/eventlisteners", Namespace) @js.native object eventlisteners extends js.Object ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/src/main/scala/snabbdom/snabbdom.scala ================================================ package snabbdom import org.scalajs.dom.{Element, Text} import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import scala.scalajs.js.annotation.JSImport.Namespace import scala.scalajs.js.| @JSImport("snabbdom", Namespace) @js.native object snabbdom extends js.Object { def init(modules: js.Array[js.Object]): js.Function2[VNode | Element, VNode, VNode] = js.native } @JSImport("snabbdom/h", Namespace) @js.native object h extends js.Function3[String, js.UndefOr[js.Any], js.UndefOr[js.Any], VNode] { def apply(selector: String, b: js.UndefOr[js.Any] = js.undefined, c: js.UndefOr[js.Any] = js.undefined): VNode = js.native } @js.native trait VNode extends js.Object { var selector: js.UndefOr[String] = js.native var data: js.UndefOr[VNodeData]= js.native var children: js.UndefOr[js.Array[VNode | String]]= js.native var text: js.UndefOr[String]= js.native var elm: js.UndefOr[Element | Text]= js.native var key: js.UndefOr[String | Double]= js.native } @js.native trait VNodeData extends js.Object ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/src/test/scala/example/SomeTest.scala ================================================ package example import org.junit.Assert._ import org.junit.Test import org.scalajs.dom.document import scala.scalajs.js class SomeTest { @Test def testSnabbdom(): Unit = { import snabbdom.{snabbdom, h, modules} val patch = snabbdom.init(js.Array( modules.props )) val vnode = h("h1", "It works": js.Any) val container = document.createElement("div") document.body.appendChild(container) patch(container, vnode) val patchedNode = document.body.lastChild assertEquals("H1", patchedNode.nodeName) assertEquals("It works", patchedNode.textContent) } } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/static/test ================================================ $ absent target/scala-2.11/scalajs-bundler/main/static-fastopt-bundle.js target/scala-2.11/scalajs-bundler/main/static-fastopt-bundle.js.map > fastOptJS::webpack $ exists target/scala-2.11/scalajs-bundler/main/static-fastopt-bundle.js target/scala-2.11/scalajs-bundler/main/static-fastopt-bundle.js.map > html index.html $ absent target/scala-2.11/scalajs-bundler/main/static-opt-bundle.js target/scala-2.11/scalajs-bundler/main/static-opt-bundle.js.map > fullOptJS::webpack $ exists target/scala-2.11/scalajs-bundler/main/static-opt-bundle.js target/scala-2.11/scalajs-bundler/main/static-opt-bundle.js.map > html index-prod.html > checkSize # Disabling source maps in Scala.js also disables it for scalajs-bundler $ delete target/scala-2.11/scalajs-bundler/main/static-fastopt-bundle.js.map target/scala-2.11/scalajs-bundler/main/static-opt-bundle.js.map > set scalaJSLinkerConfig := scalaJSLinkerConfig.value.withSourceMap(false) > fastOptJS::webpack $ absent target/scala-2.11/scalajs-bundler/main/static-fastopt-bundle.js.map > fullOptJS::webpack $ absent target/scala-2.11/scalajs-bundler/main/static-opt-bundle.js.map # webpackEmitSourceMaps controls source maps emission for the webpack task > set scalaJSLinkerConfig := scalaJSLinkerConfig.value.withSourceMap(true) > set webpackEmitSourceMaps in (Compile, fastOptJS) := false > fastOptJS::webpack $ exists target/scala-2.11/scalajs-bundler/main/static-fastopt.js.map $ absent target/scala-2.11/scalajs-bundler/main/static-fastopt-bundle.js.map > set webpackEmitSourceMaps in (Compile, fullOptJS) := false > fullOptJS::webpack $ exists target/scala-2.11/scalajs-bundler/main/static-opt.js.map $ absent target/scala-2.11/scalajs-bundler/main/static-opt-bundle.js.map > test ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/transitive/build.sbt ================================================ val sub1 = proj("sub1") .settings( npmDependencies in Compile += "react" -> "15.4.1" ) val sub2 = proj("sub2") .settings( npmDependencies in Compile += "react" -> "15.4.1" ) val sub3 = proj("sub3") .settings( npmDependencies in Compile += "react" -> "15.3.2" ) val sub4 = proj("sub4") .settings( npmDependencies in Compile += "react" -> ">=15.3.2" ) val checkPackageJson = taskKey[Unit]("Check that the package.json file does not contain duplicate entries for the 'react' dependency") val noConflicts = proj("no-conflicts") .settings( checkPackageJson := { val json = IO.read((npmUpdate in Compile).value / "package.json") assert(json.split("\n").count(_.containsSlice("react")) == 1, json) } ).dependsOn(sub1, sub2) val conflict = proj("conflict") .dependsOn(sub1, sub3) val resolution = proj("resolution") .dependsOn(sub1, sub3) .settings( npmResolutions in Compile := Map("react" -> "15.4.1") ) val delegatedToPackageManager = proj("delegatedToPackageManager") .dependsOn(sub1, sub4) def proj(id: String): Project = Project(id, file(id)) .enablePlugins(ScalaJSBundlerPlugin) .settings(scalaVersion := "2.11.12") ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/transitive/project/plugins.sbt ================================================ val scalaJSVersion = sys.props.getOrElse("scalajs.version", sys.error("'scalajs.version' environment variable is not defined")) val scalaJSBundlerVersion = sys.props.getOrElse("plugin.version", sys.error("'plugin.version' environment variable is not set")) addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % scalaJSBundlerVersion) ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/transitive/test ================================================ > no-conflicts/npmUpdate > no-conflicts/checkPackageJson -> conflict/npmUpdate > resolution/npmUpdate > delegatedToPackageManager/npmUpdate ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets/README.md ================================================ # scalajs-bundler/webpack-assets An application that uses an advanced webpack configuration Demonstrates how to: * set a custom name for the output file * demonstrates bug #192 and its fix * Use a different mode and configuration file for dev and production ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets/badconfig1.js ================================================ const ScalaJS = require("./scalajs.webpack.config"); const { merge } = require("webpack-merge"); const HtmlWebpackPlugin = require("html-webpack-plugin"); // LessLoader is requested but it is missing from npmDevDependencies const LessLoaderPlugin = require("less-loader"); const WebApp = merge(ScalaJS, { output: { filename: "library.js" }, plugins: [new HtmlWebpackPlugin()] }); module.exports = WebApp; ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets/badconfig2.js ================================================ const ScalaJS = require("./scalajs.webpack.config"); const { merge } = require("webpack-merge"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const path = require("path"); const rootDir = path.resolve(__dirname, "../../../.."); const resourcesDir = path.resolve(rootDir, "src/main/resources"); const WebApp = merge(ScalaJS, { entry: { app: [path.resolve(resourcesDir, "./entry.js")] }, module: { rules: [ { test: /\.css$/, use: ["style-loader"] // We should include css-loader } ] }, plugins: [new HtmlWebpackPlugin()] }); module.exports = WebApp; ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets/build.sbt ================================================ import com.gargoylesoftware.htmlunit.WebClient import com.gargoylesoftware.htmlunit.WebConsole.Logger name := "webpack-assets" enablePlugins(ScalaJSBundlerPlugin) scalaVersion := "2.13.2" scalaJSUseMainModuleInitializer := true // Use library mode for fastOptJS webpackBundlingMode in fastOptJS := BundlingMode.LibraryOnly() webpackConfigFile in fastOptJS := Some(baseDirectory.value / "dev.config.js") // Use application model mode for fullOptJS webpackBundlingMode in fullOptJS := BundlingMode.Application webpackConfigFile in fullOptJS := Some(baseDirectory.value / "prod.config.js") npmDevDependencies in Compile += "html-webpack-plugin" -> "5.2.0" npmDevDependencies in Compile += "webpack-merge" -> "5.7.3" npmDevDependencies in Compile += "style-loader" -> "2.0.0" npmDevDependencies in Compile += "css-loader" -> "5.0.1" npmDevDependencies in Compile += "mini-css-extract-plugin" -> "1.3.4" webpackDevServerPort := 7357 useYarn := true // HtmlUnit does not support ECMAScript 2015 scalaJSLinkerConfig ~= { _.withESFeatures(_.withUseECMAScript2015(false)) } // Check that a HTML can be loaded (and that its JavaScript can be executed) without errors InputKey[Unit]("html") := { import complete.DefaultParsers._ import scala.sys.process._ val (page, assetsCount) = (token(Space ~> StringBasic) ~ token(Space ~> IntBasic)).parsed import com.gargoylesoftware.htmlunit.WebClient val files = (webpack in (Compile, fastOptJS)).value assert(files.length == assetsCount) // Check all files are present assert(files.map(_.data.exists).forall(_ == true)) // There is only one app file assert(files.count(_.metadata.get(BundlerFileTypeAttr) == Some(BundlerFileType.Application)) == 1) // There is only one library file assert(files.count(_.metadata.get(BundlerFileTypeAttr) == Some(BundlerFileType.Library)) == 1) // There is only one library file assert(files.count(_.metadata.get(BundlerFileTypeAttr) == Some(BundlerFileType.Loader)) == 1) // And 1 asset (HTML file) assert(files.count(_.metadata.get(BundlerFileTypeAttr) == Some(BundlerFileType.Asset)) == 1) // The application is the first assert(files.head.metadata.get(BundlerFileTypeAttr) == Some(BundlerFileType.Application)) val client = new WebClient() try { val scalajsBundleDir = s"${(npmUpdate in Compile).value.absolutePath}" client.getPage(s"file://$scalajsBundleDir/$page") } finally { client.close() } } // Check that a HTML can be loaded on the output path specified InputKey[Unit]("htmlProd") := { import complete.DefaultParsers._ import scala.sys.process._ val ((page, path), assetsCount) = (token(Space ~> StringBasic) ~ token(Space ~> StringBasic) ~ token(Space ~> IntBasic)).parsed val files = (webpack in (Compile, fullOptJS)).value assert(files.length == assetsCount) // Check all files are present assert(files.map(_.data.exists).forall(_ == true)) // There is only one library file assert(files.count(_.metadata.get(BundlerFileTypeAttr) == Some(BundlerFileType.ApplicationBundle)) == 1) // The rest are assets assert(files.count(_.metadata.get(BundlerFileTypeAttr) == Some(BundlerFileType.Asset)) == assetsCount - 1) // The bundle is the first assert(files.head.metadata.get(BundlerFileTypeAttr) == Some(BundlerFileType.ApplicationBundle)) import com.gargoylesoftware.htmlunit.WebClient val client = new WebClient() try { val demoDir = s"${new File((baseDirectory in Compile).value, path).absolutePath}" client.getPage(s"file://$demoDir/$page") } finally { client.close() } } ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets/dev.config.js ================================================ const ScalaJS = require("./scalajs.webpack.config"); const { merge } = require("webpack-merge"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const WebApp = merge(ScalaJS, { output: { filename: "library.js" }, plugins: [new HtmlWebpackPlugin()] }); module.exports = WebApp; ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets/prod.config.js ================================================ const ScalaJS = require("./scalajs.webpack.config"); const { merge } = require("webpack-merge"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const path = require("path"); const rootDir = path.resolve(__dirname, "../../../.."); const resourcesDir = path.resolve(rootDir, "src/main/resources"); const WebApp = merge(ScalaJS, { entry: { app: [path.resolve(resourcesDir, "./entry.js")] }, module: { rules: [ { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] } ] }, output: { filename: "[name].[chunkhash].js", path: path.resolve(rootDir, "demo") }, plugins: [new HtmlWebpackPlugin(), new MiniCssExtractPlugin({})] }); module.exports = WebApp; ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets/project/plugins.sbt ================================================ val scalaJSVersion = sys.props.getOrElse("scalajs.version", sys.error("'scalajs.version' environment variable is not defined")) val scalaJSBundlerVersion = sys.props.getOrElse("plugin.version", sys.error("'plugin.version' environment variable is not set")) addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % scalaJSBundlerVersion) libraryDependencies += "net.sourceforge.htmlunit" % "htmlunit" % "2.46.0" ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets/src/main/resources/entry.js ================================================ import "./styles.css"; ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets/src/main/resources/styles.css ================================================ body { color: red; } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets/src/main/scala/example/Main.scala ================================================ package example object Main { def main(args: Array[String]): Unit = { println("6051a036-bfb4-4158-a171-950416b5bd9a") } } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets/test ================================================ # the server should properly start > fastOptJS::webpack # 1 assets and 3 for library, loader and app > html index.html 4 > clean # Now let's try optimized > fullOptJS::webpack > htmlProd index.html demo 4 # Error case 1 > set webpackConfigFile in fastOptJS := Some(new File("badconfig1.js")) > clean -> fastOptJS::webpack # Error case 2 > set webpackConfigFile in fastOptJS := Some(new File("badconfig2.js")) > clean -> fastOptJS::webpack ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets-cookbook/README.md ================================================ scalajs-bundler/webpack-assets-cookbook ===================== An application to demonstrate how to package up the relevant ScalaJS artifacts in a zip-file. Demonstrates how to: - use the files produced by 'fastOptJs/webpack' or 'fullOptJs/webpack' ; - create an archive containing those files ; - enrich the archive with other files if required. ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets-cookbook/build.sbt ================================================ import java.util.zip.ZipFile import com.typesafe.sbt.packager.SettingsHelper._ name := "webpack-assets" scalaVersion := "2.13.2" scalaJSUseMainModuleInitializer := true resolvers += Resolver.sonatypeRepo("snapshots") npmDependencies.in(Compile) := Seq( "react" -> "16.13.1", "react-dom" -> "16.13.1" ) webpackBundlingMode := scalajsbundler.BundlingMode.LibraryAndApplication() libraryDependencies ++= Seq( "com.github.ahnfelt" %%% "react4s" % "0.10.0-SNAPSHOT" ) //#scalajs-files // Use ScalaJs and the sbt native packager enablePlugins(ScalaJSBundlerPlugin, UniversalPlugin, UniversalDeployPlugin) // All files shall go directly into the archive rather than having a top level directory matching // the module name topLevelDirectory := None // Map all assets produced by the ScalaJs Bundler to their location within the archive mappings.in(Universal) ++= webpack.in(Compile, fullOptJS).value.map { f => f.data -> s"assets/${f.data.getName()}" } //#scalajs-files //#additional-files // Add any other required files to the archive mappings.in(Universal) ++= Seq( target.value / ("scala-" + scalaBinaryVersion.value) / "scalajs-bundler" / "main" / "node_modules" / "react" / "umd" / "react.production.min.js" -> "assets/react.production.min.js", target.value / ("scala-" + scalaBinaryVersion.value) / "scalajs-bundler" / "main" / "node_modules" / "react-dom" / "umd" / "react-dom.production.min.js" -> "assets/react-dom.production.min.js" ) //#additional-files makeDeploymentSettings(Universal, packageBin.in(Universal), "zip") TaskKey[Unit]("checkArchive") := { val expected : List[String] = List( "index.html", "assets/webpack-assets-opt-bundle.js", "assets/webpack-assets-opt-loader.js", "assets/webpack-assets-opt-library.js", "assets/webpack-assets-opt.js", "assets/react.production.min.js", "assets/react-dom.production.min.js" ) val archive = packageBin.in(Universal).value assert(archive.exists() && archive.isFile()) val entries = new ZipHelper(archive).entries assert(expected.size == entries.size) assert(expected.forall(e => entries.contains(e))) } ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets-cookbook/project/ZipHelper.scala ================================================ import java.io.File import java.util.zip.ZipFile import scala.collection.JavaConverters._ class ZipHelper(f: File) { val zipFile = new ZipFile(f) lazy val entries : List[String] = zipFile.entries().asScala.toList.map { e => e.getName() } } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets-cookbook/project/plugins.sbt ================================================ val scalaJSVersion = sys.props.getOrElse("scalajs.version", sys.error("'scalajs.version' environment variable is not defined")) val scalaJSBundlerVersion = sys.props.getOrElse("plugin.version", sys.error("'plugin.version' environment variable is not set")) addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % scalaJSBundlerVersion) addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.7.2") ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets-cookbook/src/main/scala/example/Main.scala ================================================ package example import com.github.ahnfelt.react4s._ case class HelloComponent() extends Component[NoEmit] { override def render(get: Get): Node = E.div( E.h1(Text("Hello World !")) ) } object Main { def main(args: Array[String]) : Unit = { val main = Component(HelloComponent) ReactBridge.renderToDomById(main, "content") } } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets-cookbook/src/universal/index.html ================================================ WebAsset Demo
================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-assets-cookbook/test ================================================ > clean > universal:packageBin > checkArchive ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-dev-server/build.sbt ================================================ import com.gargoylesoftware.htmlunit.WebClient import com.gargoylesoftware.htmlunit.WebConsole.Logger name := "webpack-dev-server" enablePlugins(ScalaJSBundlerPlugin) scalaVersion := "2.13.2" scalaJSUseMainModuleInitializer := true // Use a custom config file webpackConfigFile := Some(baseDirectory.value / "webpack.config.js") (npmDevDependencies in Compile) += ("html-webpack-plugin" -> "5.2.0") webpackCliVersion := "4.9.0" webpackDevServerPort := 7357 // HtmlUnit does not support ECMAScript 2015 scalaJSLinkerConfig ~= { _.withESFeatures(_.withUseECMAScript2015(false)) } // (Used by tests only) checks that a HTML can be loaded (and that its JavaScript can be executed) without errors InputKey[Unit]("html") := { import complete.DefaultParsers._ val ags = Def.spaceDelimited().parsed.toList val (page, timeout, shouldPass) = ags match { case List(page, rawTimeout, pass) => (page, rawTimeout.toInt, pass.toBoolean) case _ => sys.error("expected: page timeout") } var totalTime = 0 val dt = 100 var connected = false var gotMessage = false println("Connecting to webpack-dev-server") println("") while(totalTime < timeout && !connected) { val expected = "6051a036-bfb4-4158-a171-950416b5bd9a" val client = new WebClient() client.getWebConsole.setLogger(new Logger(){ def info(message: Any): Unit = { if (message == expected){ gotMessage = true } else { println(s"info $message") } } def debug(message: Any): Unit = println(s"debug: $message") def error(message: Any): Unit = println(s"error: $message") def trace(message: Any): Unit = println(s"trace: $message") def warn(message: Any): Unit = println(s"warn: $message") def isDebugEnabled(): Boolean = true def isErrorEnabled(): Boolean = true def isInfoEnabled(): Boolean = true def isTraceEnabled(): Boolean = true def isWarnEnabled(): Boolean = true }) try { client.getPage(s"http://localhost:7357/$page") connected = true } catch { case ex: org.apache.http.conn.HttpHostConnectException => { print("*") } } finally { client.close() } Thread.sleep(dt) totalTime += dt } assert(!shouldPass || gotMessage, "Did not get println result") } ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-dev-server/project/plugins.sbt ================================================ val scalaJSVersion = sys.props.getOrElse("scalajs.version", sys.error("'scalajs.version' environment variable is not defined")) val scalaJSBundlerVersion = sys.props.getOrElse("plugin.version", sys.error("'plugin.version' environment variable is not set")) addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % scalaJSBundlerVersion) libraryDependencies += "net.sourceforge.htmlunit" % "htmlunit" % "2.46.0" ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-dev-server/src/main/scala/example/Main.scala ================================================ package example object Main { def main(args: Array[String]): Unit = { println("6051a036-bfb4-4158-a171-950416b5bd9a") } } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-dev-server/test ================================================ # keep at least this test while the rest is disabled > fastOptJS::webpack # disabled because Webpack/webpack-cli/htmlunit don't want to work together anymore # # the server should properly start # > fastOptJS::startWebpackDevServer # > html index.html 20000 true # # the server should properly stop # > fastOptJS::stopWebpackDevServer # > html index.html 1000 false # # the server should properly terminate on exit # > fastOptJS::startWebpackDevServer # > html index.html 20000 true # -> exit # > html index.html 1000 false ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/webpack-dev-server/webpack.config.js ================================================ // Load the config generated by scalajs-bundler var config = require('./scalajs.webpack.config'); var HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = config; module.exports.plugins = [ new HtmlWebpackPlugin() ]; ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/yarn-interactive/build.sbt ================================================ scalaVersion := "2.12.8" useYarn := true yarnExtraArgs in Compile := Seq("--silent") scalaJSUseMainModuleInitializer := true npmDependencies in Compile += "neat" -> "2.1.0" enablePlugins(ScalaJSBundlerPlugin) ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/yarn-interactive/project/plugins.sbt ================================================ val scalaJSVersion = sys.props.getOrElse("scalajs.version", sys.error("'scalajs.version' environment variable is not defined")) val scalaJSBundlerVersion = sys.props.getOrElse("plugin.version", sys.error("'plugin.version' environment variable is not set")) addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % scalaJSBundlerVersion) ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/yarn-interactive/src/main/scala/example/Main.scala ================================================ package example object Main { def main(args: Array[String]): Unit = { println("yarn-interactive main") } } ================================================ FILE: sbt-scalajs-bundler/src/sbt-test/sbt-scalajs-bundler/yarn-interactive/test ================================================ > fastOptJS::webpack ================================================ FILE: sbt-web-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/NpmAssets.scala ================================================ package scalajsbundler.sbtplugin import com.typesafe.sbt.web.PathMapping import sbt._ import scalajsbundler.sbtplugin.ScalaJSBundlerPlugin.autoImport.npmUpdate object NpmAssets { /** * {{{ * npmAssets ++= NpmAssets.ofProject(client) { _ / "some-package" / "some-relevant" ** "files" }.value * }}} * * @return A task producing a sequence of files paired with their path relative to the `node_modules` directory * of `project`. * @param project The project that depends on the NPM package containing the assets to publish. * @param assets A function that finds files given the `node_modules` directory of `project` */ def ofProject(project: ProjectReference)(assets: File => PathFinder): Def.Initialize[Task[Seq[PathMapping]]] = Def.task { val nodeModules = (npmUpdate in (project, Compile)).value / "node_modules" assets(nodeModules).pair(Path.relativeTo(nodeModules)) } } ================================================ FILE: sbt-web-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/WebScalaJSBundlerPlugin.scala ================================================ package scalajsbundler.sbtplugin import com.typesafe.sbt.web.PathMapping import com.typesafe.sbt.web.pipeline.Pipeline import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ import org.scalajs.sbtplugin.Stage import sbt.Keys._ import sbt._ import webscalajs.WebScalaJS import webscalajs.WebScalaJS.autoImport._ import scala.collection.immutable.Nil import scalajsbundler.sbtplugin.ScalaJSBundlerPlugin.autoImport._ /** * If WebScalaJS is enabled, tweaks the pipelineStage so that the bundle is produced * as an sbt-web asset. * * = Tasks and Settings = * * See the [[WebScalaJSBundlerPlugin.autoImport autoImport]] member. */ object WebScalaJSBundlerPlugin extends AutoPlugin { /** * @groupname settings Settings */ object autoImport { /** * Sequence of PathMapping’s to include to sbt-web’s assets. * * @see [[scalajsbundler.sbtplugin.NpmAssets.ofProject NpmAssets.ofProject]] * @group settings */ val npmAssets: TaskKey[Seq[PathMapping]] = taskKey[Seq[PathMapping]]("Assets (resources that are not CommonJS modules) imported from the NPM packages") val NpmAssets = scalajsbundler.sbtplugin.NpmAssets } import autoImport._ override lazy val requires = WebScalaJS override lazy val projectSettings = Seq( monitoredScalaJSDirectories ++= allFrontendProjectResourceDirectories.value, scalaJSPipeline := pipelineStage.value, npmAssets := Nil ) val allFrontendProjectResourceDirectories: Def.Initialize[Seq[File]] = Def.settingDyn { val projectRefs = scalaJSProjects.value.map(Project.projectToRef) projectRefs.map { project => Def.setting { (resourceDirectories in Compile in project).value } }.foldLeft(Def.setting(Seq.empty[File]))((acc, resourceDirectories) => Def.setting(acc.value ++ resourceDirectories.value) ) } val bundlesWithSourceMaps: Def.Initialize[Task[Seq[(File, String)]]] = Def.settingDyn { val projects = scalaJSProjects.value.map(Project.projectToRef) Def.task { // ((file, relative-path), sourceMapsEnabled) val bundles: Seq[((File, String), Boolean)] = projects .map { project => Def.settingDyn { val sjsStage = (scalaJSStage in project).value match { case Stage.FastOpt => fastOptJS case Stage.FullOpt => fullOptJS } Def.task { val files = (webpack in (project, Compile, sjsStage)).value val clientTarget = (npmUpdate in (project, Compile)).value val sourceMapsEnabled = (finallyEmitSourceMaps in (project, Compile, sjsStage)).value files.map(_.data).pair(Path.relativeTo(clientTarget)).map((_, sourceMapsEnabled)) } } } .foldLeft(Def.task(Seq.empty[((File, String), Boolean)]))((acc, bundleFiles) => Def.task(acc.value ++ bundleFiles.value)) .value bundles.flatMap { case ((file, path), bundleSourceMapsEnabled) => val sourceMapFile = file.getParentFile / (file.name ++ ".map") if (bundleSourceMapsEnabled && sourceMapFile.exists) { val sourceMapPath = path ++ ".map" Seq(file -> path, sourceMapFile -> sourceMapPath) } else Seq(file -> path) } } } val pipelineStage: Def.Initialize[Task[Pipeline.Stage]] = Def.taskDyn { val npmAssetsMappings = npmAssets.value val include = (includeFilter in scalaJSPipeline).value val exclude = (excludeFilter in scalaJSPipeline).value val bundleMappings = bundlesWithSourceMaps.value val sourcemapScalaFiles = WebScalaJS.sourcemapScalaFiles.value Def.task { mappings: Seq[PathMapping] => val filtered = filterMappings(mappings, include, exclude) filtered ++ bundleMappings ++ sourcemapScalaFiles ++ npmAssetsMappings } } def filterMappings(mappings: Seq[PathMapping], include: FileFilter, exclude: FileFilter): Seq[PathMapping] = for { (file, path) <- mappings if include.accept(file) && !exclude.accept(file) } yield file -> path } ================================================ FILE: sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play/build.sbt ================================================ val client = project.in(file("client")) .enablePlugins(ScalaJSBundlerPlugin) .settings( // Required to solve https://github.com/scala/bug/issues/11955 scalaVersion := "2.13.4", scalaJSUseMainModuleInitializer := true, libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "1.0.0", npmDependencies in Compile ++= Seq( "snabbdom" -> "0.5.3", "font-awesome" -> "4.7.0", "url-loader" -> "0.5.9" ), ivyLoggingLevel := UpdateLogging.Quiet ) val server = project.in(file("server")) .enablePlugins(PlayScala, WebScalaJSBundlerPlugin) .disablePlugins(PlayLayoutPlugin) .settings( // Required to solve https://github.com/scala/bug/issues/11955 scalaVersion := "2.13.4", libraryDependencies ++= Seq( "com.vmunier" %% "scalajs-scripts" % "1.1.4", "org.scalatestplus.play" %% "scalatestplus-play" % "5.1.0" % Test ), scalaJSProjects := Seq(client), pipelineStages in Assets := Seq(scalaJSPipeline), pipelineStages := Seq(digest, gzip), // Expose as sbt-web assets some files retrieved from the NPM packages of the `client` project npmAssets ++= NpmAssets.ofProject(client) { modules => (modules / "font-awesome").allPaths }.value, ivyLoggingLevel := UpdateLogging.Quiet ) val play = project.in(file(".")) .aggregate(client, server) .settings( ivyLoggingLevel := UpdateLogging.Quiet ) ================================================ FILE: sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play/client/src/main/scala/example/Main.scala ================================================ package example import snabbdom.{snabbdom, modules, h} import scala.scalajs.js import org.scalajs.dom.document object Main { def main(args: Array[String]): Unit = { val patch = snabbdom.init(js.Array(modules.props, modules.eventlisteners)) val view = h("p", "Hello, world!": js.Any) patch(document.body.querySelector("div"), view) } } ================================================ FILE: sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play/client/src/main/scala/snabbdom/modules/modules.scala ================================================ package snabbdom.modules import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import scala.scalajs.js.annotation.JSImport.Namespace @JSImport("snabbdom/modules/class", Namespace) @js.native object `class` extends js.Object @JSImport("snabbdom/modules/props", Namespace) @js.native object props extends js.Object @JSImport("snabbdom/modules/style", Namespace) @js.native object style extends js.Object @JSImport("snabbdom/modules/eventlisteners", Namespace) @js.native object eventlisteners extends js.Object ================================================ FILE: sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play/client/src/main/scala/snabbdom/snabbdom.scala ================================================ package snabbdom import org.scalajs.dom.{Element, Text} import scala.scalajs.js import scala.scalajs.js.annotation.JSImport import scala.scalajs.js.annotation.JSImport.Namespace import scala.scalajs.js.| @JSImport("snabbdom", Namespace) @js.native object snabbdom extends js.Object { def init(modules: js.Array[js.Object]): js.Function2[VNode | Element, VNode, VNode] = js.native } @JSImport("snabbdom/h", Namespace) @js.native object h extends js.Function3[String, js.UndefOr[js.Any], js.UndefOr[js.Any], VNode] { def apply(selector: String, b: js.UndefOr[js.Any] = js.undefined, c: js.UndefOr[js.Any] = js.undefined): VNode = js.native } @js.native trait VNode extends js.Object { var selector: js.UndefOr[String] = js.native var data: js.UndefOr[VNodeData]= js.native var children: js.UndefOr[js.Array[VNode | String]]= js.native var text: js.UndefOr[String]= js.native var elm: js.UndefOr[Element | Text]= js.native var key: js.UndefOr[String | Double]= js.native } @js.native trait VNodeData extends js.Object ================================================ FILE: sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play/project/plugins.sbt ================================================ val scalaJSVersion = sys.props.getOrElse("scalajs.version", sys.error("'scalajs.version' environment variable is not defined")) val scalaJSBundlerVersion = sys.props.getOrElse("plugin.version", sys.error("'plugin.version' environment variable is not set")) addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) addSbtPlugin("ch.epfl.scala" % "sbt-web-scalajs-bundler" % scalaJSBundlerVersion) addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.9.0") addSbtPlugin("com.typesafe.sbt" % "sbt-gzip" % "1.0.2") addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.4") ivyLoggingLevel in ThisBuild := UpdateLogging.Quiet ================================================ FILE: sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play/server/src/main/resources/application.conf ================================================ play { crypto.secret = "abc123" application.loader = "example.Loader" } ================================================ FILE: sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play/server/src/main/resources/router.routes ================================================ GET / example.ExampleController.index GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset) ================================================ FILE: sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play/server/src/main/scala/example/ExampleController.scala ================================================ package example import play.api.mvc.{AbstractController, ControllerComponents} import play.twirl.api.StringInterpolation class ExampleController(cc: ControllerComponents) extends AbstractController(cc) { val index = { val result = Ok( html"""
App is not loaded.
${scalajs.html.scripts("client", controllers.routes.Assets.versioned(_).toString, name => getClass.getResource(s"/public/$name") != null)} """ ) Action(result) } } ================================================ FILE: sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play/server/src/main/scala/example/Loader.scala ================================================ package example import play.api.ApplicationLoader.Context import play.api.{Application, ApplicationLoader, BuiltInComponentsFromContext} import play.filters.HttpFiltersComponents import router.Routes class Loader extends ApplicationLoader { def load(context: Context): Application = new ExampleComponents(context).application } class ExampleComponents(context: Context) extends BuiltInComponentsFromContext(context) with HttpFiltersComponents with controllers.AssetsComponents { val controller = new ExampleController(controllerComponents) val router = new Routes(httpErrorHandler, controller, assets) } ================================================ FILE: sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play/server/src/test/resources/logback.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play/server/src/test/scala/example/ExampleSpec.scala ================================================ package example import org.scalatestplus.play.guice.GuiceOneServerPerSuite import org.scalatestplus.play.{HtmlUnitFactory, OneBrowserPerSuite, PlaySpec} import play.api.ApplicationLoader.Context import play.api.Environment class ExampleSpec extends PlaySpec with GuiceOneServerPerSuite with OneBrowserPerSuite with HtmlUnitFactory { implicit override lazy val app = (new Loader).load(Context.create(Environment.simple())) "The application" must { "load assets imported from NPM modules" in { go to s"http://localhost:$port/assets/font-awesome/css/font-awesome.min.css" pageSource must include("Font Awesome") } } } ================================================ FILE: sbt-web-scalajs-bundler/src/sbt-test/sbt-web-scalajs-bundler/play/test ================================================ > server/assets $ exists client/target/scala-2.13/scalajs-bundler/main/client-fastopt-bundle.js $ absent client/target/scala-2.13/scalajs-bundler/main/client-opt-bundle.js $ delete client/target/scala-2.13/scalajs-bundler/main/client-fastopt-bundle.js > set scalaJSStage in Global := FullOptStage > server/assets $ exists client/target/scala-2.13/scalajs-bundler/main/client-opt-bundle.js $ absent client/target/scala-2.13/scalajs-bundler/main/client-fastopt-bundle.js $ delete client/target/scala-2.13/scalajs-bundler/main/client-opt-bundle.js > set scalaJSStage in Global := FastOptStage > server/test > client/test > server/stage ================================================ FILE: scalajs-bundler-linker/src/main/scala/scalajsbundler/bundlerlinker/BundlerLinkerImpl.scala ================================================ package scalajsbundler.bundlerlinker import java.nio.file.Path import org.scalajs.linker._ import org.scalajs.linker.interface._ import org.scalajs.linker.standard._ object BundlerLinkerImpl { def linker(config: StandardConfig, entryPointOutputFile: Path): Linker = { val frontend = StandardLinkerFrontend(config) val backend = new EntryPointAnalyzerBackend(config, entryPointOutputFile) StandardLinkerImpl(frontend, backend) } def clearableLinker(config: StandardConfig, entryPointOutputFile: Path): ClearableLinker = ClearableLinker(() => linker(config, entryPointOutputFile), config.batchMode) } ================================================ FILE: scalajs-bundler-linker/src/main/scala/scalajsbundler/bundlerlinker/EntryPointAnalyzerBackend.scala ================================================ package scalajsbundler.bundlerlinker import scala.concurrent.{ExecutionContext, Future} import scala.collection.JavaConverters._ import java.nio.charset.StandardCharsets import java.nio.file.{Files, Path} import org.scalajs.logging.Logger import org.scalajs.linker.interface._ import org.scalajs.linker.standard._ final class EntryPointAnalyzerBackend(linkerConfig: StandardConfig, entryPointOutputFile: Path) extends LinkerBackend { private val standard = StandardLinkerBackend(linkerConfig) val coreSpec: CoreSpec = standard.coreSpec val symbolRequirements: SymbolRequirement = standard.symbolRequirements def injectedIRFiles: Seq[IRFile] = standard.injectedIRFiles def emit(moduleSet: ModuleSet, output: OutputDirectory, logger: Logger)( implicit ec: ExecutionContext): Future[Report] = { val modules = moduleSet.modules.flatMap(_.externalDependencies).toSet Files.write(entryPointOutputFile, modules.toIterable.asJava, StandardCharsets.UTF_8) standard.emit(moduleSet, output, logger) } }