Skip to content

Commit

Permalink
Merge pull request #119 from jeroentervoorde/master
Browse files Browse the repository at this point in the history
Support for custom webpack config in reload workflow
  • Loading branch information
julienrf authored Apr 26, 2017
2 parents d361615 + 73ea4cd commit a3d585b
Show file tree
Hide file tree
Showing 19 changed files with 396 additions and 25 deletions.
57 changes: 57 additions & 0 deletions manual/src/ornate/cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,63 @@ More fine-grained control over the list of monitored files is possible by overri
You can find a working example of custom configuration file
[here](https://github.com/scalacenter/scalajs-bundler/blob/master/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/master/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
Expand Down
8 changes: 4 additions & 4 deletions manual/src/ornate/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,16 @@ code executable by web browsers takes time. This can be a problem if you rely on
(like the one of Play framework, for instance), because the reloading time can go up to 30 seconds.

You can get a faster “change source and reload application” workflow by setting the `enableReloadWorkflow`
key to `true`.
key to `true`. An alternative way to invoke reload workflow is using the `fastOptJs::webpackReload` task.

The reload workflow replaces the `fastOptJS::webpack` task implementation with a different one, that does not use
webpack to process the output of the Scala.js compilation. Instead, it pre-bundles the modules imported by your
application and exposes them to the global namespace. Since these dependencies can then be resolved from the global
namespace, the output of Scala.js is just concatenated after the contents of the pre-bundling process.

> {.note}
> As soon as `enableReloadWorkflow` is true `fastOptJS::webpack` does **not** use webpack and therefore
> the custom webpack configuration file is ignored.
It is possible to configure an alternative webpack configuration file which is used for building the bundle using the
"webpackConfigFile in webpackReload" setting . The configuration file may not contain 'entry' nor 'output' configuration
but can be used to for loaders etc.

### Tasks and Settings {#tasks-and-settings}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.scalajs.sbtplugin.Loggers
import org.scalajs.sbtplugin.ScalaJSPlugin.AutoImport._
import sbt._

import scalajsbundler.Webpack.copyToWorkingDir
import scalajsbundler.util.{Commands, JS}

/**
Expand Down Expand Up @@ -127,10 +128,15 @@ object ReloadWorkflow {
workingDir: File,
entryPoint: File,
bundleFile: File,
customWebpackConfigFile: Option[File],
webpackResources: Seq[File],
logger: Logger
): Unit = {
logger.info("Pre-bundling dependencies")

webpackResources.foreach(copyToWorkingDir(workingDir))
val customConfigFile = customWebpackConfigFile.map(copyToWorkingDir(workingDir))

val depsFileContent =
JS.block(
imports.map { moduleName =>
Expand All @@ -139,7 +145,12 @@ object ReloadWorkflow {
)
IO.write(entryPoint, depsFileContent.show)

Webpack.run(entryPoint.absolutePath, bundleFile.absolutePath)(workingDir, logger)
customConfigFile match {
case Some(configFile) =>
Webpack.run("--config", configFile.getAbsolutePath, entryPoint.absolutePath, bundleFile.absolutePath)(workingDir, logger)
case None =>
Webpack.run(entryPoint.absolutePath, bundleFile.absolutePath)(workingDir, logger)
}

()
}
Expand Down
18 changes: 9 additions & 9 deletions sbt-scalajs-bundler/src/main/scala/scalajsbundler/Webpack.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import scalajsbundler.util.{Commands, JS}

object Webpack {

def copyToWorkingDir(targetDir: File)(file: File): File = {
val copy = targetDir / file.name
IO.copyFile(file, copy)
copy
}

/**
* Writes the webpack configuration file
*
Expand Down Expand Up @@ -84,20 +90,14 @@ object Webpack {
def bundle(
generatedWebpackConfigFile: File,
customWebpackConfigFile: Option[File],
webpackResources: Seq[File],
entries: Seq[(String, File)],
targetDir: File,
log: Logger
): Seq[File] = {

val configFile =
customWebpackConfigFile match {
case Some(file) =>
val configFileCopy = targetDir / file.name
IO.copyFile(file, configFileCopy)
configFileCopy
case None =>
generatedWebpackConfigFile
}
webpackResources.foreach(copyToWorkingDir(targetDir))
val configFile = customWebpackConfigFile.map(copyToWorkingDir(targetDir)).getOrElse(generatedWebpackConfigFile)

log.info("Bundling the application with its NPM dependencies")
Webpack.run("--config", configFile.absolutePath)(targetDir, log)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ object ReloadWorkflowTasks {
Def.task {
val targetDir = (crossTarget in stage).value
val logger = streams.value.log

val entryPointFile = targetDir / "scalajsbundler-entry-point.js"
val bundleFile = targetDir / "scalajsbundler-deps.js" // Don’t need to differentiate between stages because the dependencies should not be different between fastOptJS and fullOptJS
val webpackCfgFile = (webpackConfigFile in webpackReload).value
val webpackResourcesFiles = webpackResources.value.get

val importedModules =
ReloadWorkflow.findImportedModules(
linker,
Expand All @@ -59,6 +63,8 @@ object ReloadWorkflowTasks {
targetDir,
entryPointFile,
bundleFile,
webpackCfgFile,
webpackResourcesFiles,
streams.value.log
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import sbt.Keys._
import sbt._

import scalajsbundler.ExternalCommand.install
import scalajsbundler.Webpack.copyToWorkingDir
import scalajsbundler._

/**
Expand Down Expand Up @@ -158,12 +159,36 @@ object ScalaJSBundlerPlugin extends AutoPlugin {
val webpack: TaskKey[Seq[File]] =
taskKey[Seq[File]]("Bundle the output of a Scala.js stage using webpack")

/**
* Bundles the output of a Scala.js stage using the reload workflow.
*
* This is equivalent to running fastOptJS::webpack with reloadWorkflow := true.
* This task must be scoped by the Scala.js fastOptJS stage.
*
* For instance, to bundle the output of `fastOptJS`, run the following task from the sbt shell:
*
* {{{
* fastOptJS::webpackReload
* }}}
*
* To use a custom webpack configuration file use:
*
* {{{
* webpackConfigFile in webpackReload := Some(baseDirectory.value / "webpack-reload.config.js"),
* }}}
*
* @group tasks
*/
val webpackReload: TaskKey[Seq[File]] =
taskKey[Seq[File]]("Bundle the output of a Scala.js stage by appending the generated javascript to the pre-bundled dependencies")


/**
* configuration file to use with webpack. By default, the plugin generates a
* configuration file, but you can supply your own file via this setting. Example of use:
*
* {{{
* webpackConfigFile in fullOptJS := Some(baseDirectory.value / "my.prod.webpack.config.js")
* webpackConfigFile in fullOptJS := Some(baseDirectory.value / "my.dev.webpack.config.js")
* }}}
*
* You can find more insights on how to write a custom configuration file in the
Expand All @@ -174,6 +199,23 @@ object ScalaJSBundlerPlugin extends AutoPlugin {
val webpackConfigFile: SettingKey[Option[File]] =
settingKey[Option[File]]("Configuration file to use with webpack")

/**
* Webpack configuration files to copy to the target directory. These files can be merged into the main
* configuration file.
*
* By default all .js files in the project base directory are copied:
*
* {{{
* baseDirectory.value * "*.js"
* }}}
*
* How to share these configuration files among your webpack config files is documented in the
* [[http://scalacenter.github.io/scalajs-bundler/cookbook.html#shared-config cookbook]].
*/
val webpackResources: SettingKey[PathFinder] =
settingKey[PathFinder]("Webpack resources to copy to target directory (defaults to *.js)")


/**
* List of entry bundles to generate. By default it generates just one bundle
* for your main class.
Expand Down Expand Up @@ -213,7 +255,7 @@ object ScalaJSBundlerPlugin extends AutoPlugin {
* webpack launch in `webpack` task.
*
* Defaults to an empty `Seq`.
*
*
* @group settings
* @see [[webpackMonitoredFiles]]
*/
Expand All @@ -222,7 +264,7 @@ object ScalaJSBundlerPlugin extends AutoPlugin {

/**
* List of files, monitored for webpack launch.
*
*
* By default includes the following files:
* - Generated `package.json`
* - Generated webpack config
Expand All @@ -246,8 +288,14 @@ object ScalaJSBundlerPlugin extends AutoPlugin {
* reduces the delays when live-reloading the application on source modifications. Defaults
* to `false`.
*
* Note that the “reload workflow” does '''not''' use the custom webpack configuration file,
* if any.
* Note that the “reload workflow” does uses the custom webpack configuration file scoped to
* the webpackReload task.
*
* For example:
*
* {{{
* webpackConfigFile in webpackReload := Some(baseDirectory.value / "webpack-reload.config.js"),
* }}}
*
* @group settings
*/
Expand Down Expand Up @@ -283,7 +331,7 @@ object ScalaJSBundlerPlugin extends AutoPlugin {

/**
* Additional arguments to webpack-dev-server.
*
*
* Defaults to an empty list.
*
* @see [[startWebpackDevServer]]
Expand Down Expand Up @@ -384,6 +432,8 @@ object ScalaJSBundlerPlugin extends AutoPlugin {

webpackConfigFile := None,

webpackResources := baseDirectory.value * "*.js",

// Include the manifest in the produced artifact
(products in Compile) := (products in Compile).dependsOn(scalaJSBundlerManifest).value,

Expand Down Expand Up @@ -465,6 +515,10 @@ object ScalaJSBundlerPlugin extends AutoPlugin {
else webpackTask(fastOptJS)
}.value,

webpackReload in fastOptJS := Def.taskDyn {
ReloadWorkflowTasks.webpackTask(configuration.value, fastOptJS)
}.value,

npmUpdate := {
val log = streams.value.log
val targetDir = (crossTarget in npmUpdate).value
Expand Down Expand Up @@ -546,6 +600,12 @@ object ScalaJSBundlerPlugin extends AutoPlugin {
val sjsOutputName = sjsOutput.name.stripSuffix(".js")
val bundle = targetDir / s"$sjsOutputName-bundle.js"

val customWebpackConfigFile = (webpackConfigFile in Test).value
val webpackResourceFiles = webpackResources.value.get

webpackResourceFiles.foreach(copyToWorkingDir(targetDir))
val customConfigFile = customWebpackConfigFile.map(copyToWorkingDir(targetDir))

val writeTestBundleFunction =
FileFunction.cached(
streams.value.cacheDirectory / "test-loader",
Expand All @@ -554,7 +614,14 @@ object ScalaJSBundlerPlugin extends AutoPlugin {
logger.info("Writing and bundling the test loader")
val loader = targetDir / s"$sjsOutputName-loader.js"
JsDomTestEntries.writeLoader(sjsOutput, loader)
Webpack.run(loader.absolutePath, bundle.absolutePath)(targetDir, logger)

customConfigFile match {
case Some(configFile) =>
Webpack.run("--config", configFile.getAbsolutePath, loader.absolutePath, bundle.absolutePath)(targetDir, logger)
case None =>
Webpack.run(loader.absolutePath, bundle.absolutePath)(targetDir, logger)
}

Set.empty
}
writeTestBundleFunction(Set(sjsOutput))
Expand Down Expand Up @@ -670,10 +737,8 @@ object ScalaJSBundlerPlugin extends AutoPlugin {
val customConfigOption = (webpackConfigFile in stageTask).value
val generatedConfig = (scalaJSBundlerWebpackConfig in stageTask).value

val config = customConfigOption match {
case Some(customConfig) => targetDir / customConfig.name
case None => generatedConfig
}
webpackResources.value.get.foreach(copyToWorkingDir(targetDir))
val config = customConfigOption.map(copyToWorkingDir(targetDir)).getOrElse(generatedConfig)

// To match `webpack` task behavior
val workDir = targetDir
Expand Down Expand Up @@ -707,6 +772,7 @@ object ScalaJSBundlerPlugin extends AutoPlugin {
val targetDir = npmUpdate.value
val generatedWebpackConfigFile = (scalaJSBundlerWebpackConfig in stage).value
val customWebpackConfigFile = (webpackConfigFile in stage).value
val webpackResourceFiles = webpackResources.value.get
val entries = (webpackEntries in stage).value
val monitoredFiles = (webpackMonitoredFiles in stage).value

Expand All @@ -718,6 +784,7 @@ object ScalaJSBundlerPlugin extends AutoPlugin {
Webpack.bundle(
generatedWebpackConfigFile,
customWebpackConfigFile,
webpackResourceFiles,
entries,
targetDir,
log
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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.
Loading

0 comments on commit a3d585b

Please sign in to comment.