-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from johnhungerford/Documentation
Documentation
- Loading branch information
Showing
2 changed files
with
314 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,323 @@ | ||
# sbt-vite | ||
|
||
An SBT plugin to run and test Scala.js projects using vite | ||
An SBT plugin to build and test Scala.js projects using vite. | ||
|
||
## Usage | ||
|
||
This plugin requires sbt 1.0.0+ | ||
This plugin requires sbt 1.0.0+. | ||
|
||
To use sbt-vite in your project, add the following line to `projects/plugins.sbt`: | ||
|
||
```sbt | ||
addSbtPlugin("io.github.johnhungerford.sbt.vite" % "sbt-vite" % "0.0.7") | ||
``` | ||
|
||
In `build.sbt`, include `SbtVitePlugin` in `.enablePlugins(...)` in any Scala.js project | ||
that needs to be bundled with JavaScript dependencies, and configure the Scala.js plugin | ||
to use ECMAScript modules: | ||
|
||
```sbt | ||
scalaJSLinkerConfig ~= { | ||
_.withModuleKind(ModuleKind.ESModule) | ||
} | ||
``` | ||
|
||
### Building | ||
|
||
Add the following files to the root directory of your project or sub-project: | ||
|
||
`index.html` | ||
```html | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>sbt-vite</title> | ||
</head> | ||
<body> | ||
<script type="module" src="/main.js"></script> | ||
</body> | ||
</html> | ||
``` | ||
|
||
If your Scala.js project is runnable (i.e., if it has a `def main` entrypoint and | ||
`scalaJSUseMainModuleInitializer := true` in build.sbt), you can simply import the | ||
application to be run as follows | ||
|
||
`main.js`: | ||
```javascript | ||
// 'scalajs:main.js' will be resolved by vite to the output of the Scala.js linker | ||
import 'scalajs:main.js' | ||
``` | ||
|
||
Otherwise you can simply import any exported objects from your Scala.js project as | ||
follows: | ||
|
||
`main.js`: | ||
```javascript | ||
import { someLib, otherLib } from 'scalajs:main.js'; | ||
|
||
... | ||
|
||
someLib.doSomething(); | ||
otherLib.doSomethingElse(); | ||
|
||
... | ||
``` | ||
|
||
Once you have your html and js entrypoints in place, you can run the following to | ||
generate a web bundle: | ||
|
||
```shell | ||
sbt viteBuild | ||
``` | ||
|
||
This will compile your project, generate an appropriate vite configuration, and run | ||
vite on all artifacts. By default, the bundle will persisted at | ||
`[project-directory]/target/scala-[x.x.x]/sbt-vite/bundle` | ||
|
||
### Testing | ||
|
||
Run `test` for regular unit tests. | ||
Run tests using the usual command: | ||
|
||
```shell | ||
sbt test | ||
``` | ||
|
||
This will use vite to bundle the linked JavaScript test executable with any dependencies | ||
prior to running it. | ||
|
||
## Dependency management | ||
|
||
This plugin would not be of much use if it did not resolve dependencies properly. There | ||
are currently three modes of dependency management. | ||
|
||
### Fully managed | ||
|
||
By default, sbt-vite will manage all dependencies. To explicitly enable this | ||
mode, add the following setting to `build.sbt`: | ||
|
||
```sbt | ||
viteDependencyManagement := DependencyManagement.Managed(NpmManager.Npm) | ||
``` | ||
|
||
You can also pass `NpmManager.Yarn` or `NpmManager.Pnpm` to use `yarn` or `pnpm` to | ||
install npm dependencies instead of `npm` (default). | ||
|
||
You can declare npm dependencies to be used in your project using the following two | ||
sbt settings: | ||
|
||
```sbt | ||
npmDependencies ++= Seq( | ||
"react" -> "^18.2.0", | ||
"react-dom" -> "^18.2.0", | ||
) | ||
|
||
npmDevDependencies ++= Seq( | ||
"vite-plugin-eslint" -> "^1.8.1", | ||
) | ||
``` | ||
|
||
For `npmDevDependencies` in particular, be sure to use `++=` instead of `:=`, as | ||
sbt-vite includes several dependencies required to execute the build. | ||
|
||
#### Other non-Scala.js sources | ||
|
||
In addition to bundling npm modules, sbt-vite will bundle Scala.js outputs with | ||
imported sources, such as `JavaScript`, `TypeScript`, `css`, `less`, and others. | ||
|
||
Source files and directories can be declared for inclusion using the `viteOtherSources` | ||
setting: | ||
|
||
```sbt | ||
viteOtherSources := Seq( | ||
Location.FromProjectRoot(file("src/main/typescript")), | ||
Location.FromProjectRoot(file("src/main/styles")), | ||
Location.FromRoot(file("common/typescript")), | ||
Location.FromRoot(file("common/styles")), | ||
Location.FromRoot(file("common/index.html")), | ||
Location.FromRoot(file("common/main.js")), | ||
) | ||
``` | ||
|
||
(See [appendix below](#location)) | ||
|
||
For any declared source that points to a directory, sbt-vite will copy all the files | ||
and directories within it to the build directory prior to running `vite`. Any declared | ||
source that is a file will be copied directly to the build directory. | ||
|
||
Accordingly, any sources declared in your build can be imported as expected: | ||
|
||
```scala | ||
// This will import either from [project]/src/main/typescript/someDir/someTypeScriptModule.ts | ||
// or from common/typescript/someDir/someTypeScriptModule.ts | ||
@js.native | ||
@JSImport("/someDir/someTypeScriptModule", JSImport.Default) | ||
object TypeScriptImport extends js.Object | ||
|
||
// This will import either from [project]/src/main/styles/someStyle.css or from | ||
// common/styles/someStyle.css | ||
@js.native | ||
@JSImport("/someStyle.css?inline", JSImport.Namespace) | ||
object CssImport extends js.Object | ||
``` | ||
|
||
These imports will work correctly in JS and TS sources as well: | ||
|
||
```javascript | ||
import someModule from '/someDir/someTypeScriptModule'; | ||
import '/someStyle.css'; | ||
``` | ||
|
||
### Manual | ||
|
||
When dependency management is set to `Manual`, you will be responsible for managing any | ||
dependencies needed for vite to bundle your project. sbt-vite will neither install any | ||
npm packages nor copy over any source directories. Instead, sbt-vite will simply run from | ||
`viteProjectRoot` (by default set to the root directory of your project or sub-project, | ||
see [appendix below](#location)), from which it will expect to be able to resolve any | ||
imports, whether via local sources or `node_modules`. | ||
|
||
Note that when using manual mode, `viteBuild` and `test` will fail unless you install | ||
`vite`, `lodash`, `rollup-plugin-sourcemaps`, and `vite-plugin-scalajs` as dev | ||
dependencies. To have sbt-vite install these for you, use `InstallOnly` dependency | ||
management (see [below](#install-only)). | ||
|
||
To enable manual dependency management, using the following setting in `build.sbt`: | ||
|
||
```sbt | ||
viteDependencyManagement := DependencyManagement.Manual | ||
``` | ||
|
||
### Install-only | ||
|
||
Install-only dependency management is like manual mode, only it will automatically | ||
install any dependencies declared in `npmDependencies` and `npmDevDependencies` in | ||
your `viteProjectRoot` directory. This will ensure that any requirements needed simply | ||
to run the vite build commands will be in place, as these are included by default in | ||
`npmDevDependencies`. | ||
|
||
## Customization | ||
|
||
To allow users to customize the build process sbt-vite provides various settings for | ||
overriding configurations. | ||
|
||
### Build command customization | ||
|
||
Every build command executed by sbt-vite can be passed custom arguments or options as | ||
well as custom environment variables using the following settings: | ||
|
||
```sbt | ||
// Pass additional options or arguments to the "vite build" command | ||
viteExtraArgs += "--mode=development" | ||
|
||
// Set environment variables for the "vite build" command | ||
viteEnvironment += "NODE_ENV" -> "development" | ||
|
||
// Pass additional options or arguments to the "npm install" command | ||
npmExtrArgs += "--legacy-peer-deps" | ||
|
||
// Set environment variables for the "npm install" command | ||
npmEnvironment += "NPM_CONFIG_PREFIX" -> "global_node_modules" | ||
|
||
// Pass additional options or arguments to the "pnpm add" command | ||
pnpmExtrArgs += "--save-exact" | ||
|
||
// Set environment variables for the "pnpm add" command | ||
pnpmEnvironment += "NPM_CONFIG_PREFIX" -> "global_node_modules" | ||
|
||
// Pass additional options or arguments to the "yarn add" command | ||
yarnExtrArgs += "--audit" | ||
|
||
// Set environment variables for the "yarn add" command | ||
yarnEnvironment += "NPM_CONFIG_PREFIX" -> "global_node_modules" | ||
``` | ||
|
||
### Vite config overrides | ||
|
||
sbt-vite generates configuration scripts with reasonable defaults for full builds | ||
(i.e., `viteBuild`, which is an alias for `Compile / viteBuild`), and test builds | ||
(i.e., `Test / viteBuild`, which prepares a bundle to be executed by `Test / test`). | ||
|
||
To override these defaults, you can use the setting `viteConfigSources` to provide | ||
one or more configuration scripts that will be merged with the defaults, allowing | ||
you to override various settings. Note that the following configuration properties | ||
cannot be overridden: | ||
1. `root` | ||
2. `build.rollupOptions.input`, and | ||
3. `build.rollupOptions.output.dir` | ||
|
||
`viteConfigSources` must specify valid javascript files that provide a default export | ||
of one of the following two forms: | ||
1. a simple [vite configuration object](https://github.com/vitejs/vite/blob/997a6951450640fed8cf19e58dce0d7a01b92392/packages/vite/src/node/config.ts#L127) | ||
2. a function that consumes a [vite environment configuration](https://github.com/vitejs/vite/blob/997a6951450640fed8cf19e58dce0d7a01b92392/packages/vite/src/node/config.ts#L78) | ||
and returns a [vite configuration](https://github.com/vitejs/vite/blob/997a6951450640fed8cf19e58dce0d7a01b92392/packages/vite/src/node/config.ts#L127). | ||
|
||
Note that neither of these should be wrapped in `defineConfig`, as this will be called | ||
after merging imported overrides. | ||
|
||
Note also that the `viteConfigSources` will be merged in order, so later sources in the | ||
`Seq` will have precedence over prior sources. | ||
|
||
`viteConfigSources` can be scoped to `Compile` and `Test` to provide different | ||
customizations for your full build and for tests. | ||
|
||
#### Example | ||
|
||
The following vite config source provides overrides to support JSX (React), | ||
bundle source maps (disabled by default except in tests), and break out several | ||
library dependencies into separate chunks: | ||
|
||
`build.sbt`: | ||
```sbt | ||
Compile / viteConfigSources += Location.FromRoot(file("vite.config-build.js")) | ||
``` | ||
|
||
'vite.config-build.js': | ||
```javascript | ||
import react from '@vitejs/plugin-react'; | ||
|
||
import sourcemaps from 'rollup-plugin-sourcemaps'; | ||
|
||
Run `scripted` for [sbt script tests](http://www.scala-sbt.org/1.x/docs/Testing-sbt-plugins.html). | ||
export default (env)=> ({ | ||
// Array properties will concat on merge, so this will be added | ||
// to plugins, instead of overwriting | ||
plugins: [ | ||
react(), | ||
], | ||
build: { | ||
sourcemap: true, | ||
rollupOptions: { | ||
plugins: [sourcemaps()], | ||
output: { | ||
strict: false, | ||
chunkFileNames: '[name]-[hash:10].js', | ||
manualChunks: { | ||
lodash: ['lodash'], | ||
react: ['react'], | ||
'react-dom': ['react-dom'], | ||
'react-router-dom': ['react-router-dom'], | ||
} | ||
} | ||
}, | ||
}, | ||
}); | ||
``` | ||
|
||
### CI | ||
## Appendices | ||
|
||
The generated project uses [sbt-github-actions](https://github.com/djspiewak/sbt-github-actions) as a plugin to generate workflows for GitHub actions. For full details of how to use it [read this](https://github.com/djspiewak/sbt-github-actions/blob/main/README.md) | ||
### Location | ||
|
||
### Publishing | ||
In order to identify directories and files more easily, sbt-vite uses a custom type | ||
`Location`, which allows you to specify paths relative to commonly used base directories: | ||
|
||
1. publish your source to GitHub | ||
2. Follow the instructions in [sbt-ci-release](https://github.com/olafurpg/sbt-ci-release/blob/main/readme.md) to create a sonatype account and setup your keys | ||
3. `sbt ci-release` | ||
4. [Add your plugin to the community plugins list](https://github.com/sbt/website#attention-plugin-authors) | ||
5. [Claim your project an Scaladex](https://github.com/scalacenter/scaladex-contrib#claim-your-project) | ||
1. `Location.Root`: the base directory of the root project | ||
2. `Location.ProjectRoot`: the base directory of the currently scoped project | ||
3. `Location.FromRoot(file)`: provide a `File` relative to `Location.Root`. Note | ||
that `file` must have a relative path. | ||
4. `Location.FromProject(file)`: provide a `File` relative to `Location.ProjectRoot`. | ||
`file` must have a relative path. | ||
5. `Location.FromCwd(file)`: provide a `File` relative to the current working directory | ||
(which will presumably always be the same as `Location.Root`). `file` need not | ||
be relative, so use this to specify a location using an absolute path. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters