Skip to content

Commit d65082e

Browse files
authored
Merge branch 'main' into scala-steward-dry-run
2 parents 184fca8 + c95000d commit d65082e

20 files changed

Lines changed: 391 additions & 1 deletion

File tree

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
continue-on-error: ${{ matrix.experimental }}
3737
env:
3838
SBT_CHECK_TASKS: scalafmtSbtCheck scalafmtCheckAll test
39-
SBT_SCRIPTED_TARGETS: ${{ matrix.jdk == 8 && format('sbt-scalajs-esbuild/*-{0} sbt-scalajs-esbuild-electron/basic-project-{0} sbt-scalajs-esbuild-electron/e2e-test-playwright-node-{0} sbt-scalajs-esbuild-electron/electron-builder-{0}', matrix.test-suffix) || format('*/*-{0}', matrix.test-suffix) }}
39+
SBT_SCRIPTED_TARGETS: ${{ matrix.jdk == 8 && format('sbt-scalajs-esbuild/*-{0} sbt-scalajs-esbuild-electron/basic-project-{0} sbt-scalajs-esbuild-electron/e2e-test-playwright-node-{0} sbt-scalajs-esbuild-electron/electron-builder-{0} sbt-scalajs-esbuild-electron/electron-forge-{0}', matrix.test-suffix) || format('*/*-{0}', matrix.test-suffix) }}
4040
steps:
4141
- uses: actions/checkout@v7
4242
- uses: coursier/cache-action@v8
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import org.scalajs.linker.interface.ModuleInitializer
2+
import org.scalajs.sbtplugin.Stage
3+
import scalajs.esbuild.electron.EsbuildElectronProcessConfiguration
4+
import scala.sys.process._
5+
6+
enablePlugins(ScalaJSEsbuildElectronPlugin)
7+
8+
ThisBuild / scalaVersion := "2.13.18"
9+
10+
scalaJSModuleInitializers := Seq(
11+
ModuleInitializer
12+
.mainMethodWithArgs("example.Main", "main")
13+
.withModuleID("main"),
14+
ModuleInitializer
15+
.mainMethodWithArgs("example.Preload", "main")
16+
.withModuleID("preload"),
17+
ModuleInitializer
18+
.mainMethodWithArgs("example.Renderer", "main")
19+
.withModuleID("renderer")
20+
)
21+
22+
Compile / esbuildElectronProcessConfiguration := new EsbuildElectronProcessConfiguration(
23+
"main",
24+
Set("preload"),
25+
Set("renderer")
26+
)
27+
28+
// Suppress meaningless 'multiple main classes detected' warning
29+
Compile / mainClass := None
30+
31+
libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.8.1"
32+
33+
val esbuildElectronForgePackage =
34+
taskKey[Unit]("Generate package directory with Electron Forge")
35+
val esbuildElectronForgeMakeDistributable =
36+
taskKey[Unit]("Package in distributable format with Electron Forge")
37+
38+
val perConfigSettings = Seq(Stage.FastOpt, Stage.FullOpt).flatMap { stage =>
39+
val stageTask = stage match {
40+
case Stage.FastOpt => fastLinkJS
41+
case Stage.FullOpt => fullLinkJS
42+
}
43+
44+
def fn(args: List[String]) = Def.task {
45+
val log = streams.value.log
46+
47+
(stageTask / esbuildBundle).value
48+
49+
val targetDir = (esbuildStage / crossTarget).value
50+
51+
val exitValue = Process(
52+
"node" :: "node_modules/@electron-forge/cli/dist/electron-forge.js" :: args,
53+
targetDir
54+
).run(log).exitValue()
55+
if (exitValue != 0) {
56+
sys.error(s"Nonzero exit value: $exitValue")
57+
} else ()
58+
}
59+
60+
Seq(
61+
stageTask / esbuildElectronForgePackage := fn("package" :: Nil).value,
62+
stageTask / esbuildElectronForgeMakeDistributable := fn("make" :: Nil).value
63+
)
64+
}
65+
inConfig(Compile)(perConfigSettings)
66+
inConfig(Test)(perConfigSettings)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// See https://www.electronforge.io/configuration
2+
module.exports = {
3+
// esbuild already bundles the app into `out`, so Forge must output
4+
// elsewhere to avoid clobbering (and recursing into) that directory.
5+
outDir: "forge-out",
6+
packagerConfig: {
7+
// Everything the app needs at runtime is bundled by esbuild into `out`
8+
// (electron is provided by the runtime), so only `out` and package.json
9+
// need to be packaged - everything else is build-time scaffolding.
10+
ignore: (path) =>
11+
path !== "" && path !== "/package.json" && !/^\/out(\/|$)/.test(path)
12+
},
13+
makers: [
14+
{
15+
name: "@electron-forge/maker-zip"
16+
}
17+
]
18+
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
6+
<title>Hello World!</title>
7+
</head>
8+
<body>
9+
<h1>Hello World!</h1>
10+
We are using Node.js <span id="node-version"></span>,
11+
Chromium <span id="chrome-version"></span>,
12+
and Electron <span id="electron-version"></span>.
13+
14+
<!-- You can also require other files to run in this process -->
15+
<script type="module" src="./renderer.js"></script>
16+
17+
<h1 id="css-hook"></h1>
18+
</body>
19+
</html>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "electron-project",
3+
"private": true,
4+
"version": "0.0.0",
5+
"main": "out/main.js",
6+
"repository": {
7+
"url": "git+https://github.com/ptrdom/scalajs-esbuild.git"
8+
},
9+
"devDependencies": {
10+
"esbuild": "0.28.1",
11+
"parse5": "8.0.1",
12+
"electron": "43.0.0",
13+
"@electron-forge/cli": "7.11.2",
14+
"@electron-forge/maker-zip": "7.11.2"
15+
},
16+
"comment": "extract-zip@2 (yauzl@2) silently truncates zip extraction on Node >=24.16, so @electron/get produces an incomplete Electron download and packaging emits nothing while exiting 0. Redirect it to the drop-in @electron-internal/extract-zip until Electron Forge 8 ships the fix. See https://github.com/electron/forge/issues/4277.",
17+
"overrides": {
18+
"extract-zip": "npm:@electron-internal/extract-zip@1.0.3"
19+
}
20+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* styles.css */
2+
3+
/* Add styles here to customize the appearance of your app */
4+
5+
#css-hook::after {
6+
content: 'CSS WORKS!'
7+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.22.0")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
addSbtPlugin("me.ptrdom" % "sbt-scalajs-esbuild-electron" % "0.1.3")
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package example
2+
3+
import example.facade.electron.BrowserWindow
4+
import example.facade.electron.BrowserWindowConfig
5+
import example.facade.electron.ElectronGlobals.app
6+
import example.facade.electron.WebPreferences
7+
import example.facade.node.NodeGlobals.__dirname
8+
import example.facade.node.NodeGlobals.process
9+
import example.facade.node.Path.join
10+
11+
import scala.scalajs.js
12+
import scala.scalajs.js.|
13+
14+
object Main extends App {
15+
// Create the browser window.
16+
def createWindow(): Unit = {
17+
val mainWindow = new BrowserWindow(new BrowserWindowConfig {
18+
override val height = 600
19+
override val width = 800
20+
override val webPreferences = new WebPreferences {
21+
override val preload = join(__dirname, "preload.js")
22+
}
23+
})
24+
25+
// and load the index.html of the app.
26+
process.env.DEV_SERVER_URL
27+
.asInstanceOf[js.UndefOr[String]]
28+
.toOption
29+
.fold(
30+
mainWindow.loadFile(join(__dirname, "../out", "index.html"))
31+
)(url => mainWindow.loadURL(url))
32+
33+
// Open the DevTools.
34+
// mainWindow.webContents.openDevTools()
35+
}
36+
37+
// This method will be called when Electron has finished
38+
// initialization and is ready to create browser windows.
39+
// Some APIs can only be used after this event occurs.
40+
app
41+
.whenReady()
42+
.`then`((_ => {
43+
createWindow()
44+
45+
// On macOS it's common to re-create a window in the app when the
46+
// dock icon is clicked and there are no other windows open.
47+
app.on(
48+
"activate",
49+
() => {
50+
if (BrowserWindow.getAllWindows().length == 0) createWindow()
51+
}
52+
)
53+
}): js.Function1[Unit, Unit | js.Thenable[Unit]])
54+
55+
// Quit when all windows are closed, except on macOS. There, it's common
56+
// for applications and their menu bar to stay active until the user quits
57+
// explicitly with Cmd + Q.
58+
app.on(
59+
"window-all-close",
60+
() => {
61+
if (process.platform != "darwin") app.quit()
62+
}
63+
)
64+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package example
2+
3+
import example.facade.node.NodeGlobals.process
4+
import org.scalajs.dom
5+
import org.scalajs.dom.document
6+
import org.scalajs.dom.window
7+
8+
import scala.scalajs.js
9+
10+
/** The preload script runs before. It has access to web APIs as well as
11+
* Electron's renderer process modules and some polyfilled Node.js functions.
12+
*
13+
* https://www.electronjs.org/docs/latest/tutorial/sandbox
14+
*/
15+
object Preload extends App {
16+
window.addEventListener(
17+
"DOMContentLoaded",
18+
{ (_: dom.Event) =>
19+
val replaceText = (selector: String, text: String) => {
20+
val element = document.getElementById(selector)
21+
if (element != null) element.innerText = text
22+
}
23+
24+
js.Array("chrome", "node", "electron")
25+
.foreach(`type` =>
26+
replaceText(
27+
s"${`type`}-version",
28+
process.versions.get(`type`).orNull
29+
)
30+
)
31+
}
32+
)
33+
34+
document.addEventListener(
35+
"DOMContentLoaded",
36+
{ (_: dom.Event) =>
37+
val h1 = document.createElement("h1")
38+
h1.textContent = "PRELOAD WORKS!"
39+
document.body.append(h1)
40+
}
41+
)
42+
}

0 commit comments

Comments
 (0)