11
22import com.github.gradle.node.npm.task.NpmTask
33import com.github.gradle.node.npm.task.NpxTask
4- import groovy.json.JsonOutput
54
65plugins {
76 kotlin(" jvm" )
@@ -25,28 +24,30 @@ dependencies {
2524/*
2625Third-party library bundling
2726
28- Libraries from node_modules are copied into src/main/resources/render/lib/
29- at Gradle build time, so they are bundled in the JAR and copied to the
30- Quarkdown output directory at compilation time.
31-
32- A nested third-party-manifest.json is generated at the end so that the
33- Kotlin runtime can enumerate JAR resources (JAR directories are not listable).
27+ Libraries from node_modules are copied into build/thirdparty/
28+ at Gradle build time, so they are included in the distribution archive
29+ and loaded from the filesystem at runtime.
3430
3531To add a new library:
36321. Add the npm package to package.json
37332. Add a LibrarySpec entry to `librariesToBundle` (or to `fontsourcePackages` for fonts)
38343. For non-font libraries, add a ThirdPartyLibrary subclass in ThirdPartyLibrary.kt
3935*/
4036
41- val libDir = projectDir.resolve(" src/main/resources/render/lib" )
37+ val thirdPartyDir =
38+ layout.buildDirectory
39+ .dir(" thirdparty" )
40+ .get()
41+ .asFile
4242val nodeModules = projectDir.resolve(" node_modules" )
4343val scssThirdParty = projectDir.resolve(" src/main/scss/thirdparty" )
44+ val staticFontsDir = projectDir.resolve(" src/main/fonts" )
4445
4546/* *
46- * Declarative specification for copying a library from `node_modules` into `lib /`.
47+ * Declarative specification for copying a library from `node_modules` into `build/thirdparty /`.
4748 * Multiple specs may share the same [target] (their files are merged into one directory).
4849 *
49- * @param target output directory name under `lib /`
50+ * @param target output directory name under `build/thirdparty /`
5051 * @param source path relative to `node_modules/`
5152 * @param includes glob patterns to select files (empty = everything)
5253 */
@@ -57,7 +58,7 @@ data class LibrarySpec(
5758)
5859
5960/* *
60- * All libraries to copy from `node_modules` into JAR resources .
61+ * All libraries to copy from `node_modules` into the distribution .
6162 * To add a new library, append a `LibrarySpec` entry here.
6263 */
6364val librariesToBundle =
@@ -72,7 +73,7 @@ val librariesToBundle =
7273 )
7374
7475/* *
75- * @fontsource packages to bundle. Each is placed in its own `lib /fonts/{name}/` directory
76+ * @fontsource packages to bundle. Each is placed in its own `build/thirdparty /fonts/{name}/` directory
7677 * with auto-discovered latin woff2 variants and a generated `fonts.css`.
7778 * SCSS layout themes select which fonts they need via `@import url(...)`.
7879 */
@@ -109,19 +110,19 @@ val bundleHighlightJs =
109110 " --format=iife" ,
110111 " --global-name=hljs" ,
111112 " --minify" ,
112- " --outfile=${libDir .resolve(" highlight.js/highlight.min.js" )} " ,
113+ " --outfile=${thirdPartyDir .resolve(" highlight.js/highlight.min.js" )} " ,
113114 ),
114115 )
115116 }
116117
117118/* *
118119 * Main bundling task: copies libraries, bundles fontsource fonts,
119- * copies hljs SCSS partials, and generates the nested manifest JSON .
120+ * and copies hljs SCSS partials.
120121 */
121122val bundleThirdParty =
122123 tasks.register<DefaultTask >(" bundleThirdParty" ) {
123124 group = " build"
124- description = " Bundles third-party libraries from node_modules into JAR resources "
125+ description = " Bundles third-party libraries from node_modules into the distribution "
125126 dependsOn(tasks.npmInstall)
126127 dependsOn(bundleHighlightJs)
127128
@@ -131,28 +132,32 @@ val bundleThirdParty =
131132 from(nodeModules.resolve(source)) {
132133 if (includes.isNotEmpty()) include(includes)
133134 }
134- into(libDir .resolve(target))
135+ into(thirdPartyDir .resolve(target))
135136 }
136137 }
137138
138139 fontsourcePackages.forEach { fontName ->
139140 bundleFontsource(" fonts/$fontName " , listOf (fontName))
140141 }
141142
143+ // Copy static (non-npm) font files from the source tree.
144+ copy {
145+ from(staticFontsDir)
146+ into(thirdPartyDir.resolve(" fonts" ))
147+ }
148+
142149 scssThirdParty.mkdirs()
143150 hljsScssPartials.forEach { (source, partialName) ->
144151 nodeModules
145152 .resolve(" highlight.js/styles/$source " )
146153 .copyTo(scssThirdParty.resolve(" _$partialName .scss" ), overwrite = true )
147154 }
148-
149- generateNestedManifest()
150155 }
151156 }
152157
153158/* *
154159 * Auto-discovers latin-subset woff2 files from the given @fontsource packages,
155- * copies them into the given [targetSubdir] under `lib /`, and generates a `fonts.css`
160+ * copies them into the given [targetSubdir] under `build/thirdparty /`, and generates a `fonts.css`
156161 * with `@font-face` declarations derived from the file naming convention.
157162 *
158163 * @fontsource files follow the pattern `{font}-latin-{weight}-{style}.woff2`,
@@ -162,7 +167,7 @@ fun bundleFontsource(
162167 targetSubdir : String ,
163168 fontNames : List <String >,
164169) {
165- val targetDir = libDir .resolve(targetSubdir)
170+ val targetDir = thirdPartyDir .resolve(targetSubdir)
166171 val cssBuilder = StringBuilder ()
167172
168173 // Matches e.g. "lato-latin-400-normal.woff2" -> (lato, 400, normal)
@@ -201,45 +206,6 @@ fun bundleFontsource(
201206 targetDir.resolve(" fonts.css" ).writeText(cssBuilder.toString())
202207}
203208
204- /* *
205- * Generates `third-party-manifest.json` as a nested JSON tree mirroring the `lib/` directory structure.
206- * Files at each level are listed under `_files`; subdirectories become nested objects.
207- *
208- * ```json
209- * {
210- * "katex": {
211- * "_files": ["katex.min.css", "katex.min.js"],
212- * "fonts": { "_files": ["KaTeX_Main-Regular.woff2"] }
213- * },
214- * "fonts": {
215- * "latex": { "_files": ["fonts.css", "ComputerModern-Serif-Regular.woff"] }
216- * }
217- * }
218- * ```
219- */
220- fun generateNestedManifest () {
221- fun dirToJson (dir : File ): Map <String , Any > =
222- buildMap {
223- dir
224- .listFiles()
225- ?.filter { it.isFile && it.name != " third-party-manifest.json" }
226- ?.map { it.name }
227- ?.sorted()
228- ?.takeIf { it.isNotEmpty() }
229- ?.let { put(" _files" , it) }
230-
231- dir
232- .listFiles()
233- ?.filter { it.isDirectory }
234- ?.sortedBy { it.name }
235- ?.forEach { put(it.name, dirToJson(it)) }
236- }
237-
238- libDir
239- .resolve(" third-party-manifest.json" )
240- .writeText(JsonOutput .prettyPrint(JsonOutput .toJson(dirToJson(libDir))))
241- }
242-
243209// SCSS and TypeScript bundling
244210
245211val scssDir = projectDir.resolve(" src/main/scss" )
@@ -287,7 +253,16 @@ val bundleTypeScript =
287253tasks.processResources {
288254 dependsOn(tasks.compileSass)
289255 dependsOn(bundleTypeScript)
290- dependsOn(bundleThirdParty)
256+ // bundleThirdParty no longer needed for processResources since libraries are not in the JAR.
257+ // It is still needed for compileSass (hljs SCSS partials).
258+
259+ // Write the absolute path of the third-party directory into a resource file
260+ // so the CLI can locate it at runtime regardless of how it was launched.
261+ doLast {
262+ val propsDir = destinationDir.resolve(" render" )
263+ propsDir.mkdirs()
264+ propsDir.resolve(" thirdparty.path" ).writeText(thirdPartyDir.absolutePath)
265+ }
291266}
292267
293268// Tests
0 commit comments