Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support locally hosted CDN dependencies #485

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,19 +256,20 @@ and limitations for this exporter.
The look and feel of the generated site can be customized with several additional view properties in the C4
architecture model:

| Property name | Description | Default | Example |
|-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|------------------------------------------------------|
| `generatr.style.colors.primary` | Primary site color, used for header bar background and active menu background. | `#333333` | `#485fc7` |
| `generatr.style.colors.secondary` | Secondary site color, used for font color in header bar and for active menu. | `#cccccc` | `#ffffff` |
| `generatr.style.faviconPath` | Site logo location relative to the configured `assets` folder. When configured, the logo image will be place on the left side in the header bar. This requires the `--assets-dir` switch when generating the site and the corresponding file to be available in the `assets` folder. | | `site/favicon.ico` |
| `generatr.style.logoPath` | Site favicon location relative to the configured `assets` folder. When configured, the favicon will be set for all generated pages. This requires the `--assets-dir` switch when generating the site and the corresponding file to be available in the `assets` folder. | | `site/logo.png` |
| `generatr.style.customStylesheet` | URL to hosted custom stylesheet or path to custom stylesheet file (location relative to the configured `assets` folder). When configured this css file will be loaded for all pages. When using a path to a file the `--assets-dir` switch must be used when generating the site and the corresponding file is available in the `assets` folder. | | `site/custom.css` or 'https://uri.example/custom.css |
| `generatr.search.language` | Indexing/stemming language for the search index. See [Lunr language support](https://github.com/olivernn/lunr-languages) | `en` | `nl` |
| `generatr.markdown.flexmark.extensions` | Additional extensions to the markdown generator to add new markdown capabilities. [More Details](https://avisi-cloud.github.io/structurizr-site-generatr/main/extended-markdown-features/) | Tables | `Tables,Admonition` |
| `generatr.svglink.target` | Specifies the link target for element links in the exported svg | `_top` | `_self` |
| `generatr.site.exporter` | Specifies the UML exporter, can be `c4` (uses the `C4PlantUMLExporter`) or `structurizr` (uses the `StructurizrPlantUMLExporter`) | `c4` | `structurizr` |
| `generatr.site.externalTag` | Software systems containing this tag will be considered external | | |
| `generatr.site.nestGroups` | Will show software systems in the left side navigator in collapsable groups | `false` | `true` |
| Property name | Description | Default | Example |
|-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|------------------------------------------------------|
| `generatr.style.colors.primary` | Primary site color, used for header bar background and active menu background. | `#333333` | `#485fc7` |
| `generatr.style.colors.secondary` | Secondary site color, used for font color in header bar and for active menu. | `#cccccc` | `#ffffff` |
| `generatr.style.faviconPath` | Site logo location relative to the configured `assets` folder. When configured, the logo image will be place on the left side in the header bar. This requires the `--assets-dir` switch when generating the site and the corresponding file to be available in the `assets` folder. | | `site/favicon.ico` |
| `generatr.style.logoPath` | Site favicon location relative to the configured `assets` folder. When configured, the favicon will be set for all generated pages. This requires the `--assets-dir` switch when generating the site and the corresponding file to be available in the `assets` folder. | | `site/logo.png` |
| `generatr.style.customStylesheet` | URL to hosted custom stylesheet or path to custom stylesheet file (location relative to the configured `assets` folder). When configured this css file will be loaded for all pages. When using a path to a file the `--assets-dir` switch must be used when generating the site and the corresponding file is available in the `assets` folder. | | `site/custom.css` or 'https://uri.example/custom.css |
| `generatr.search.language` | Indexing/stemming language for the search index. See [Lunr language support](https://github.com/olivernn/lunr-languages) | `en` | `nl` |
| `generatr.markdown.flexmark.extensions` | Additional extensions to the markdown generator to add new markdown capabilities. [More Details](https://avisi-cloud.github.io/structurizr-site-generatr/main/extended-markdown-features/) | Tables | `Tables,Admonition` |
| `generatr.svglink.target` | Specifies the link target for element links in the exported svg | `_top` | `_self` |
| `generatr.site.exporter` | Specifies the UML exporter, can be `c4` (uses the `C4PlantUMLExporter`) or `structurizr` (uses the `StructurizrPlantUMLExporter`) | `c4` | `structurizr` |
| `generatr.site.externalTag` | Software systems containing this tag will be considered external | | |
| `generatr.site.nestGroups` | Will show software systems in the left side navigator in collapsable groups | `false` | `true` |
| `generatr.site.cdn` | Specifies the CDN base location for fetching NPM packages for browser runtime dependencies. Defaults to jsDelivr, but can be changed to e.g. an on-premise location. | `https://cdn.jsdelivr.net/npm` | `https://cdn.my-company/npm` |

See the included example for usage of some those properties in the
[C4 architecture model example](https://github.com/avisi-cloud/structurizr-site-generatr/blob/main/docs/example/workspace.dsl#L163).
Expand Down
1 change: 1 addition & 0 deletions docs/example/workspace.dsl
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ workspace "Big Bank plc" "This is an example workspace to illustrate the key fea
"generatr.site.exporter" "c4"
"generatr.site.externalTag" "External System"
"generatr.site.nestGroups" "false"
"generatr.site.cdn" "https://cdn.jsdelivr.net/npm"
}

systemlandscape "SystemLandscape" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package nl.avisi.structurizr.site.generatr.site.model

import nl.avisi.structurizr.site.generatr.includedSoftwareSystems
import nl.avisi.structurizr.site.generatr.site.GeneratorContext
import nl.avisi.structurizr.site.generatr.site.views.CDN

abstract class PageViewModel(protected val generatorContext: GeneratorContext) {
val pageTitle: String by lazy {
Expand All @@ -12,6 +13,7 @@ abstract class PageViewModel(protected val generatorContext: GeneratorContext) {
else
pageSubTitle
}
val cdn by lazy { CDN(generatorContext.workspace) }
val favicon by lazy { FaviconViewModel(generatorContext, this) }
val customStylesheet by lazy { CustomStylesheetViewModel(generatorContext, this) }
val headerBar by lazy { HeaderBarViewModel(this, generatorContext) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +1,66 @@
package nl.avisi.structurizr.site.generatr.site.views

import com.structurizr.Workspace
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import java.lang.IllegalStateException

class CDN {
companion object {
private const val CDN_BASE = "https://cdn.jsdelivr.net/npm/"
private val json = Json { ignoreUnknownKeys = true }
class CDN(val workspace: Workspace) {
private val cdnBaseURL = getCdnBaseUrl()
private val json = Json { ignoreUnknownKeys = true }

private val packageJson = object {}.javaClass.getResource("/package.json")?.readText()
?: throw IllegalStateException("package.json not found")
private val packageJson = object {}.javaClass.getResource("/package.json")?.readText()
?: throw IllegalStateException("package.json not found")

private val dependencies = json.parseToJsonElement(packageJson).jsonObject["dependencies"]
?.jsonObject
?.map { Dependency(it.key, it.value.jsonPrimitive.content) }
?: throw IllegalStateException("dependencies element not found in package.json")
private val dependencies = json.parseToJsonElement(packageJson).jsonObject["dependencies"]
?.jsonObject
?.map { Dependency(cdnBaseURL, it.key, it.value.jsonPrimitive.content) }
?: throw IllegalStateException("dependencies element not found in package.json")

fun bulmaCss() = dependencies.single { it.name == "bulma" }.let {
"${it.baseUrl()}/css/bulma.min.css"
}
fun bulmaCss() = dependencies.single { it.name == "bulma" }.let {
"${it.baseUrl()}/css/bulma.min.css"
}

fun katexJs() = dependencies.single { it.name == "katex" }.let {
"${it.baseUrl()}/dist/katex.min.js"
}
fun katexJs() = dependencies.single { it.name == "katex" }.let {
"${it.baseUrl()}/dist/katex.min.js"
}

fun katexCss() = dependencies.single { it.name == "katex" }.let {
"${it.baseUrl()}/dist/katex.min.css"
}
fun katexCss() = dependencies.single { it.name == "katex" }.let {
"${it.baseUrl()}/dist/katex.min.css"
}

fun lunrJs() = dependencies.single { it.name == "lunr" }.let {
"${it.baseUrl()}/lunr.min.js"
}
fun lunrJs() = dependencies.single { it.name == "lunr" }.let {
"${it.baseUrl()}/lunr.min.js"
}

fun lunrLanguagesStemmerJs() = dependencies.single { it.name == "lunr-languages" }.let {
"${it.baseUrl()}/min/lunr.stemmer.support.min.js"
}
fun lunrLanguagesStemmerJs() = dependencies.single { it.name == "lunr-languages" }.let {
"${it.baseUrl()}/min/lunr.stemmer.support.min.js"
}

fun lunrLanguagesJs(language: String) = dependencies.single { it.name == "lunr-languages" }.let {
"${it.baseUrl()}/min/lunr.$language.min.js"
}
fun lunrLanguagesJs(language: String) = dependencies.single { it.name == "lunr-languages" }.let {
"${it.baseUrl()}/min/lunr.$language.min.js"
}

fun mermaidJs() = dependencies.single { it.name == "mermaid" }.let {
"${it.baseUrl()}/dist/mermaid.esm.min.mjs"
}
fun mermaidJs() = dependencies.single { it.name == "mermaid" }.let {
"${it.baseUrl()}/dist/mermaid.esm.min.mjs"
}

fun svgpanzoomJs() = dependencies.single { it.name == "svg-pan-zoom" }.let {
"${it.baseUrl()}/dist/svg-pan-zoom.min.js"
}
fun svgpanzoomJs() = dependencies.single { it.name == "svg-pan-zoom" }.let {
"${it.baseUrl()}/dist/svg-pan-zoom.min.js"
}

fun webfontloaderJs() = dependencies.single { it.name == "webfontloader" }.let {
"${it.baseUrl()}/webfontloader.js"
}
fun webfontloaderJs() = dependencies.single { it.name == "webfontloader" }.let {
"${it.baseUrl()}/webfontloader.js"
}

fun getCdnBaseUrl() = workspace.views.configuration.properties
.getOrDefault("generatr.site.cdn", "https://cdn.jsdelivr.net/npm")
.trimEnd('/')

@Serializable
data class Dependency(val name: String, val version: String) {
fun baseUrl() = "$CDN_BASE$name@$version"
data class Dependency(val cdnBaseURL: String, val name: String, val version: String) {
fun baseUrl() = "$cdnBaseURL/$name@$version"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,25 @@ fun BODY.markdownAdmonitionScript(viewModel: PageViewModel) {
) { }
}

fun HEAD.katexStylesheet() {
fun HEAD.katexStylesheet(cdn: CDN) {
// loading KaTeX as global on a webpage: https://katex.org/docs/browser.html#loading-as-global
unsafe {
raw("""
<link rel="stylesheet" href="${CDN.katexCss()}" crossorigin="anonymous">
<link rel="stylesheet" href="${cdn.katexCss()}" crossorigin="anonymous">
""")
}
}

fun HEAD.katexScript() {
fun HEAD.katexScript(cdn: CDN) {
// loading KaTeX as global on a webpage: https://katex.org/docs/browser.html#loading-as-global
unsafe {
raw("""
<script defer src="${CDN.katexJs()}" crossorigin="anonymous"></script>
<script defer src="${cdn.katexJs()}" crossorigin="anonymous"></script>
""")
}
}

fun HEAD.katexFonts() {
fun HEAD.katexFonts(cdn: CDN) {
// loading KaTeX as global on a webpage: https://katex.org/docs/browser.html#loading-as-global
unsafe {
raw("""
Expand All @@ -50,7 +50,7 @@ fun HEAD.katexFonts() {
},
};
</script>
<script defer src="${CDN.webfontloaderJs()}" crossorigin="anonymous"></script>
<script defer src="${cdn.webfontloaderJs()}" crossorigin="anonymous"></script>
""")
}
}
Expand All @@ -72,7 +72,7 @@ fun BODY.mermaidScript(viewModel: PageViewModel) {
// Simple full example, how to include Mermaid: https://mermaid.js.org/config/usage.html#simple-full-example
script(type = "module") {
unsafe {
raw("import mermaid from '${CDN.mermaidJs()}';")
raw("import mermaid from '${viewModel.cdn.mermaidJs()}';")
}
}
}
Loading