-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathBunBuildModule.mill
More file actions
131 lines (118 loc) · 4.98 KB
/
BunBuildModule.mill
File metadata and controls
131 lines (118 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package build
import mill.*, scalalib.*, scalajslib.*
import mill.scalajslib.api.*
import mill.api.BuildCtx
import java.util.zip.GZIPOutputStream
import java.io.{FileInputStream, FileOutputStream, BufferedInputStream, BufferedOutputStream}
/**
* Extension trait for ScalaJSModule that adds bun build functionality.
*
* Mix this trait into your ScalaJSModule to add a `bunBuild` task that:
* 1. Runs fullLinkJS to generate optimized JavaScript
* 2. Extracts the output JS file path
* 3. Runs bun build on that file with --outfile
* 4. Outputs to docs/public/examples/{fullModulePath}.js by default
*/
trait BunBuildModule extends ScalaJSModule {
/**
* Output file path relative to the workspace root (where build.mill is located).
* Default: docs/public/examples/{fullModulePath}.js
*
* Override this to customize the output location.
* Example: override def bunBuildOutputFile = Task { BuildCtx.workspaceRoot / "dist" / s"${moduleSegments.render}.js" }
*/
def bunBuildOutputFile: T[os.Path] = Task {
// Get the full module path (e.g., "docs.examples.laminar.components.tag")
val fullModulePath = moduleSegments.render
BuildCtx.workspaceRoot / "docs" / "public" / "examples" / s"$fullModulePath.js"
}
/**
* Additional arguments to pass to bun build.
* Default: includes --minify flag
*/
def bunBuildArgs: T[Seq[String]] = Task { Seq("--minify") }
/**
* Runs fullLinkJS, then builds the output with bun.
* Returns a PathRef to the bun build output file.
*
* The output file defaults to docs/public/examples/{fullModulePath}.js.
* Override bunBuildOutputFile to customize the location.
*/
def bunBuild: T[PathRef] = Task {
// Run fullLinkJS to get the compiled JavaScript
val report: Report = fullLinkJS()
// Silently skip if there are no public modules
report.publicModules.headOption match {
case None =>
// No modules to build, return the dest directory as a placeholder
PathRef(report.dest.path)
case Some(firstModule) =>
val jsFile = report.dest.path / firstModule.jsFileName
// Defensive check: ensure the JS file exists
if (!os.exists(jsFile)) {
// File doesn't exist, return the dest directory as a placeholder
PathRef(report.dest.path)
} else {
// Get output file path (relative to workspace root by default)
val outputFile = bunBuildOutputFile()
// Ensure output directory exists and run bun build
// Use BuildCtx.withFilesystemCheckerDisabled to allow writing/reading outside Task.dest
BuildCtx.withFilesystemCheckerDisabled {
// Ensure the parent directory exists
os.makeDir.all(outputFile / os.up)
// Run bun build with --outfile
val bunArgs = Seq(
"bun",
"build",
jsFile.toString,
"--outfile",
outputFile.toString
) ++ bunBuildArgs()
val buildResult = os.proc(bunArgs).call(
stdout = os.Inherit,
stderr = os.Inherit,
check = false
)
// Check if bun build succeeded
if (buildResult.exitCode != 0) {
// Build failed, return the dest directory as a placeholder
PathRef(report.dest.path)
} else {
// Defensive check: ensure the output file was created
if (!os.exists(outputFile)) {
// Output file not created, return the dest directory as a placeholder
PathRef(report.dest.path)
} else {
// Create gzipped version of the output file
val gzipFile = outputFile / os.up / s"${outputFile.last}.gz"
try {
val inputStream = new BufferedInputStream(new FileInputStream(outputFile.toIO))
val outputStream = new BufferedOutputStream(
new GZIPOutputStream(new FileOutputStream(gzipFile.toIO))
)
try {
val buffer = new Array[Byte](8192)
var bytesRead = 0
while ({ bytesRead = inputStream.read(buffer); bytesRead } != -1) {
outputStream.write(buffer, 0, bytesRead)
}
} finally {
inputStream.close()
outputStream.close()
}
} catch {
case e: Exception =>
// If gzip compression fails, log but don't fail the build
// The .js file is still available as fallback
println(s"Warning: Failed to create gzip file: ${e.getMessage}")
}
// Return PathRef to the output file
// PathRef reads the file, so it must be inside the disabled checker block
PathRef(outputFile)
}
}
}
}
}
}
}