-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ : manage CSS & JS external dependencies dynamically
- Loading branch information
Showing
5 changed files
with
142 additions
and
21 deletions.
There are no files selected for viewing
55 changes: 55 additions & 0 deletions
55
src/main/java/io/codeka/gaia/dashboard/controller/UIExtensionsControllerAdvice.kt
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 |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package io.codeka.gaia.dashboard.controller | ||
|
||
import org.springframework.context.ApplicationContext | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
import org.springframework.web.bind.annotation.ControllerAdvice | ||
import org.springframework.web.bind.annotation.ModelAttribute | ||
|
||
/*** | ||
* A controller advice to inject ui-extensions | ||
*/ | ||
@ControllerAdvice | ||
class UIExtensionsControllerAdvice(uiExtensions: List<UiExtension>) { | ||
|
||
private var cssExtensions: List<UiExtension> = uiExtensions.filter { it.resourcePath.endsWith(".css") } | ||
|
||
private var jsExtensions: List<UiExtension> = uiExtensions.filter { it.resourcePath.endsWith(".js") } | ||
|
||
@ModelAttribute(value = "cssExtensions", binding = false) | ||
fun getCssExtensions() = this.cssExtensions | ||
|
||
@ModelAttribute(value = "jsExtensions", binding = false) | ||
fun getJsExtensions() = this.jsExtensions | ||
|
||
} | ||
|
||
data class UiExtension(val resourcePath: String) | ||
|
||
@Configuration | ||
open class UIExtensionsConfig(){ | ||
|
||
@Bean | ||
open fun uiExtensions(applicationContext: ApplicationContext): List<UiExtension>? { | ||
val scanner = UiExtensionsScanner(applicationContext) | ||
return scanner.scan( | ||
// CSS | ||
"classpath*:/**/bootstrap/**/bootstrap.min.css", | ||
"classpath*:/**/font-awesome/**/all.css", | ||
"classpath*:/**/bootstrap-vue/**/bootstrap-vue.min.css", | ||
"classpath*:/**/vue-multiselect/**/vue-multiselect.min.css", | ||
|
||
// JS | ||
"classpath*:/**/jquery/**/jquery.min.js", | ||
"classpath*:/**/popper.js/**/umd/popper.min.js", | ||
"classpath*:/**/bootstrap/**/bootstrap.min.js", | ||
"classpath*:/**/vue/**/vue.min.js", | ||
"classpath*:/**/bootstrap-vue/**/bootstrap-vue.min.js", | ||
"classpath*:/**/marked/**/marked.min.js", | ||
"classpath*:/**/vue-multiselect/**/vue-multiselect.min.js", | ||
"classpath*:/**/momentjs/**/moment.min.js", | ||
"classpath*:/**/moment-duration-format/**/moment-duration-format.js" | ||
) | ||
} | ||
} | ||
|
30 changes: 30 additions & 0 deletions
30
src/main/java/io/codeka/gaia/dashboard/controller/UiExtensionsScanner.kt
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 |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package io.codeka.gaia.dashboard.controller | ||
|
||
import org.springframework.core.io.Resource | ||
import org.springframework.core.io.support.ResourcePatternResolver | ||
|
||
class UiExtensionsScanner(private val resolver: ResourcePatternResolver) { | ||
|
||
fun scan(vararg locations: String): List<UiExtension> { | ||
return locations | ||
.map { resolveAsset(it) } | ||
.filter { it.isReadable } | ||
.map { getResourcePath(it) } | ||
.map { UiExtension(it) } | ||
} | ||
|
||
/** | ||
* Resolves a wildcard location to a classpath Resource | ||
*/ | ||
private fun resolveAsset(location: String): Resource { | ||
return resolver.getResources(location).single() | ||
} | ||
|
||
/** | ||
* Get a resource path to be included in html (from /webjars) | ||
*/ | ||
private fun getResourcePath(resource: Resource): String { | ||
return resource.url.toString().replaceBeforeLast("/webjars","") | ||
} | ||
|
||
} |
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
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,13 +1,4 @@ | ||
<!-- javascript load --> | ||
<!-- jquery & bootstrap --> | ||
<script src="/webjars/jquery/3.0.0/jquery.min.js"></script> | ||
<script src="/webjars/popper.js/1.14.3/umd/popper.min.js"></script> | ||
<script src="/webjars/bootstrap/4.3.1/js/bootstrap.min.js"></script> | ||
<!-- vue & bootstrap-vue --> | ||
<script src="/webjars/vue/2.5.16/vue.js"></script> | ||
<script src="/webjars/bootstrap-vue/2.0.4/dist/bootstrap-vue.js"></script> | ||
<!-- other components --> | ||
<script src="/webjars/marked/0.6.2/marked.min.js"></script> | ||
<script src="/webjars/vue-multiselect/2.1.6/dist/vue-multiselect.min.js"></script> | ||
<script src="/webjars/momentjs/2.24.0/min/moment.min.js"></script> | ||
<script src="/webjars/moment-duration-format/1.3.0/lib/moment-duration-format.js"></script> | ||
<!-- external JS dependencies--> | ||
<th:block th:each="jsFile : ${jsExtensions}"> | ||
<script th:src="${jsFile.resourcePath}"></script> | ||
</th:block> |
49 changes: 49 additions & 0 deletions
49
src/test/java/io/codeka/gaia/dashboard/controller/UiExtensionsScannerTest.kt
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 |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package io.codeka.gaia.dashboard.controller | ||
|
||
import org.assertj.core.api.Assertions.assertThat | ||
import org.junit.jupiter.api.Test | ||
import org.rnorth.visibleassertions.VisibleAssertions.assertThrows | ||
import org.springframework.boot.test.context.SpringBootTest | ||
import org.springframework.context.ApplicationContext | ||
import java.util.NoSuchElementException | ||
|
||
@SpringBootTest(classes = [UIExtensionsConfig::class]) | ||
class UiExtensionsScannerTest(val applicationContext: ApplicationContext) { | ||
|
||
@Test | ||
fun `scan should find all exsting extensions`() { | ||
val scanner = UiExtensionsScanner(applicationContext) | ||
|
||
val locations = arrayOf( | ||
// CSS | ||
"classpath*:/**/bootstrap/**/bootstrap.min.css", | ||
"classpath*:/**/font-awesome/**/all.css", | ||
"classpath*:/**/bootstrap-vue/**/bootstrap-vue.min.css", | ||
"classpath*:/**/vue-multiselect/**/vue-multiselect.min.css", | ||
|
||
// JS | ||
"classpath*:/**/jquery/**/jquery.min.js", | ||
"classpath*:/**/popper.js/**/umd/popper.min.js", | ||
"classpath*:/**/bootstrap/**/bootstrap.min.js", | ||
"classpath*:/**/vue/**/vue.min.js", | ||
"classpath*:/**/bootstrap-vue/**/bootstrap-vue.min.js", | ||
"classpath*:/**/marked/**/marked.min.js", | ||
"classpath*:/**/vue-multiselect/**/vue-multiselect.min.js", | ||
"classpath*:/**/momentjs/**/moment.min.js", | ||
"classpath*:/**/moment-duration-format/**/moment-duration-format.js" | ||
) | ||
|
||
val uiExtension = scanner.scan(*locations) | ||
|
||
assertThat(uiExtension.size).isEqualTo(locations.size); | ||
} | ||
|
||
@Test | ||
fun `scan should throw an exception when an extension cannot be found`() { | ||
val scanner = UiExtensionsScanner(applicationContext) | ||
|
||
val locations = arrayOf("classpath*:/**/bootstrap/**/bootstrap.min.BOUM") | ||
|
||
assertThrows("Array is empty", NoSuchElementException::class.java) { scanner.scan(*locations) } | ||
} | ||
} |