Skip to content
This repository has been archived by the owner on Oct 12, 2021. It is now read-only.

Commit

Permalink
Add whitelist class scanning strategy (#309)
Browse files Browse the repository at this point in the history
Adds a new scanning strategy 'whitelist' which allows you to find all
instances of annotated classes in the classpath. This might be required
if the @route classes does not directly reference components (for instance
in the case of IOC frameworks.

Since when using the 'whitelist' scanning strategy all components which
conforms to the whitelist will be included in the bundle it is recommended
to manually configure the 'vaadin.whitelistedPackages' property so that it
only lists packages which has the wanted components in the bundle.

For example:

vaadin.scanStrategy = 'whitelist'
vaadin.whitelistedPackages = [
        'com.vaadin.flow.component.orderedlayout',
        'my.project.root.package'
]

Would only include the ordered layouts as well as your project packages.
  • Loading branch information
johndevs committed Nov 27, 2019
1 parent e16dfae commit 6ca2ffd
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ class VaadinFlowPluginExtension {
private static final String COMPATIBILITY_MODE_PROPERTY = 'vaadin.compatibilityMode'
private static final String VAADIN_ROOT_PACKAGE = 'com.vaadin'
private static final String GROOVY_STRING = 'groovy'
private static final String ROUTE = 'route'

private final Property<String> version
private final Property<Boolean> unsupportedVersion
private final Property<Boolean> productionMode
private final Property<Boolean> compatibilityMode
private final Property<Boolean> submitStatistics
private final ListProperty<String> whitelistedPackages
private final Property<String> scanStrategy
private final Property<String> baseTheme

private final DependencyHandler dependencyHandler
Expand All @@ -85,6 +87,7 @@ class VaadinFlowPluginExtension {
compatibilityMode = project.objects.property(Boolean)
submitStatistics = project.objects.property(Boolean)
whitelistedPackages = project.objects.listProperty(String)
scanStrategy = project.objects.property(String)
baseTheme = project.objects.property(String)

project.afterEvaluate {
Expand Down Expand Up @@ -293,6 +296,36 @@ class VaadinFlowPluginExtension {
baseTheme.set(theme)
}

/**
* Get the strategy which the classpath scanner uses to resolve dependencies
*
* The strategy can be one of the following:
* route - Uses @Route and scans all its referencing classes
* whitelist - Finds all classes that matches the whitelistedPackages filter
*
* @return
* the current strategy. By default 'route'
*/
String getScanStrategy() {
scanStrategy.getOrElse(ROUTE)
}

/**
* Set the strategy which the classpath scanner uses to resolve dependencies
*
* The strategy can be one of the following:
* route - Uses @Route and scans all its referencing classes
* whitelist - Finds all classes that matches the whitelistedPackages filter
*
*/
void setScanStrategy(String strategy) {
if (strategy in [ROUTE, 'whitelist']) {
scanStrategy.set(strategy)
} else {
throw new IllegalArgumentException("Scan strategy can either be 'route' or 'whitelist'.")
}
}

/**
* Autoconfigures repositories and dependencies
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class TranspileDependenciesTask extends DefaultTask {
@OutputDirectory
final Closure<File> appNodeModules = {
VaadinFlowPluginExtension vaadin = project.extensions.getByType(VaadinFlowPluginExtension)
vaadin.compatibilityMode ? null : Paths.get(workingDir.canonicalPath, 'dist', NODE_MODULES).toFile()
vaadin.compatibilityMode ? null : Paths.get(workingDir.canonicalPath, 'dist', NODE_MODULES).toFile()
}

@Deprecated
Expand Down Expand Up @@ -312,10 +312,10 @@ class TranspileDependenciesTask extends DefaultTask {
.substitutions(['theme': vaadin.baseTheme])
.build().write()

LOGGER.info('Searching for JS modules...')
LOGGER.info("Searching for JS modules with scan strategy '${vaadin.scanStrategy}'...")
Map<String, String> modules = [:]
LogUtils.measureTime('Scanning Js modules completed') {
modules = ClassIntrospectionUtils.findJsModules(scan)
modules = findJsModules(vaadin, scan)
}

LOGGER.info('Validating JS module imports...')
Expand All @@ -324,19 +324,19 @@ class TranspileDependenciesTask extends DefaultTask {
LOGGER.info('Checking for theme variants of JS modules...')
replaceBaseThemeModules(modules)

LOGGER.info('Search for JS imports...')
LOGGER.info("Searching for JS imports with scan strategy '${vaadin.scanStrategy}'...")
Map<String, String> jsImports = [:]
LogUtils.measureTime('Scanning Js imports completed') {
jsImports = ClassIntrospectionUtils.findJsImportsByRoute(scan)
.collectEntries { k, v -> [ (k - 'frontend://') : v ] }
.collectEntries { k, v -> [ (DOTSLASH + k) : v ] }
.findAll { k, v -> !modules.containsKey(k.toString().replace(JAVASCRIPT_FILE_TYPE, '-es6.js')) }
jsImports = findJsImports(vaadin, scan)
.collectEntries { k, v -> [(k - 'frontend://'): v] }
.collectEntries { k, v -> [(DOTSLASH + k): v] }
.findAll { k, v -> !modules.containsKey(k.toString().replace(JAVASCRIPT_FILE_TYPE, '-es6.js')) }
}

LOGGER.info('Searching for CSS imports...')
Map<String,String> cssImports = [:]
LOGGER.info("Searching for CSS imports with scan strategy '${vaadin.scanStrategy}'...")
Map<String, String> cssImports = [:]
LogUtils.measureTime('Scanning CSS imports completed') {
cssImports = ClassIntrospectionUtils.findCssImports(scan)
cssImports = findCssImports(vaadin, scan)
}

LOGGER.info('Validating CSS imports..')
Expand Down Expand Up @@ -396,6 +396,42 @@ class TranspileDependenciesTask extends DefaultTask {
}
}

@PackageScope
Map<String, String> findJsModules(VaadinFlowPluginExtension vaadin, ScanResult scan) {
switch (vaadin.scanStrategy) {
case 'route':
return ClassIntrospectionUtils.findJsModulesByRoute(scan)
case 'whitelist':
return ClassIntrospectionUtils.findJsModulesByWhitelist(scan)
default:
throw new GradleException("Scan strategy $vaadin.scanStrategy has not been implemented.")
}
}

@PackageScope
Map<String, String> findJsImports(VaadinFlowPluginExtension vaadin, ScanResult scan) {
switch (vaadin.scanStrategy) {
case 'route':
return ClassIntrospectionUtils.findJsImportsByRoute(scan)
case 'whitelist':
return ClassIntrospectionUtils.findJsImportsByWhitelist(scan)
default:
throw new GradleException("Scan strategy $vaadin.scanStrategy has not been implemented.")
}
}

@PackageScope
Map<String, String> findCssImports(VaadinFlowPluginExtension vaadin, ScanResult scan) {
switch (vaadin.scanStrategy) {
case 'route':
return ClassIntrospectionUtils.findCssImportsByRoute(scan)
case 'whitelist':
return ClassIntrospectionUtils.findCssImportsByWhitelist(scan)
default:
throw new GradleException("Scan strategy $vaadin.scanStrategy has not been implemented.")
}
}

@PackageScope
void checkIdUsage(ScanResult scan) {
if (!ignoreIdUsage) {
Expand All @@ -413,9 +449,8 @@ class TranspileDependenciesTask extends DefaultTask {

@PackageScope
void checkJsModulesInCompatibilityMode(ScanResult scan) {
Map<String,String> modules = ClassIntrospectionUtils
.findJsModules(scan)
.findAll { k, v -> !v.startsWith(VAADIN_FLOW_PACKAGE) }
VaadinFlowPluginExtension vaadin = project.extensions.getByType(VaadinFlowPluginExtension)
Map<String,String> modules = findJsModules(vaadin, scan).findAll { k, v -> !v.startsWith(VAADIN_FLOW_PACKAGE) }
if (!modules.isEmpty()) {
LOGGER.severe('Javascript modules is not supported in compatibility mode.')
LOGGER.severe('The following classes contains @JSModule annotations')
Expand All @@ -428,9 +463,8 @@ class TranspileDependenciesTask extends DefaultTask {

@PackageScope
void checkCssImportsInCompatibilityMode(ScanResult scan) {
Map<String,String> imports = ClassIntrospectionUtils
.findCssImports(scan)
.findAll { k, v -> !v.startsWith(VAADIN_FLOW_PACKAGE) }
VaadinFlowPluginExtension vaadin = project.extensions.getByType(VaadinFlowPluginExtension)
Map<String,String> imports = findCssImports(vaadin, scan).findAll { k, v -> !v.startsWith(VAADIN_FLOW_PACKAGE) }
if (!imports.isEmpty()) {
LOGGER.severe('Css imports is not supported in compatibility mode.')
LOGGER.severe('The following classes contains @CssImport annotations')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ class ClassIntrospectionUtils {
jsImports
}

/**
* Get all Javascript imports by route
*
* @since 1.3
* @param scan
* The classpath scan with the annotations
* @return
* a list of Js imports
*/
static final Map<String, String> findJsImportsByRoute(ScanResult scan) {
Map<String, String> modules = [:]
List<String> processedClasses = []
Expand All @@ -104,6 +113,25 @@ class ClassIntrospectionUtils {
modules
}

/**
* Get all Javascript imports by whitelist
*
* @since 1.3
* @param scan
* The classpath scan with the annotations
* @return
* a list of Js imports
*/
static final Map<String, String> findJsImportsByWhitelist(ScanResult scan) {
Map<String, String> jsImports = [:]
scan.getClassesWithAnnotation(JS_IMPORT_FQN).each { clz ->
clz.getAnnotationInfoRepeatable(JS_IMPORT_FQN).each {
jsImports.put(it.parameterValues.value.value.toString(), clz.name)
}
}
jsImports
}

/**
* Get all Stylesheet imports
*
Expand Down Expand Up @@ -143,14 +171,14 @@ class ClassIntrospectionUtils {
}

/**
* Find all JsModules in project
* Find all JsModules in project using route strategy
*
* @param scan
* the classpath scan with the annotations
* @return
* the values of the js modules
*/
static final Map<String, String> findJsModules(ScanResult scan) {
static final Map<String, String> findJsModulesByRoute(ScanResult scan) {
Map<String, String> modules = [:]
List<String> processedClasses = []
scan.getClassesWithAnnotation(ROUTE_FQN).each { ClassInfo ci ->
Expand All @@ -161,14 +189,36 @@ class ClassIntrospectionUtils {
}

/**
* Find all CSSImports in project
* Gets all JsModules from the whitelisted scan
*
* The whitelist is set in #VaadinFlowPluginExtension.whitelistedPackages and is performed
* when the scan is created.
*
* @param scan
* the classpath scan with the annotations
* @return
* the values of the js modules
*/
static final Map<String,String> findJsModulesByWhitelist(ScanResult scan) {
Map<String, String> modules = [:]
scan.getClassesWithAnnotation(JS_MODULE_FQN).each { ClassInfo ci ->
ci.getAnnotationInfoRepeatable(JS_MODULE_FQN).each { AnnotationInfo a ->
modules[a.parameterValues.value.value.toString()] = ci.name
}
}
modules
}

/**
* Find all CSSImports in project using route strategy
*
* @since 1.3
* @param scan
* the classpath scan with the annoations
* the classpath scan with the annotations
* @return
* the values of the css imports
*/
static final Map<String,String> findCssImports(ScanResult scan) {
static final Map<String,String> findCssImportsByRoute(ScanResult scan) {
Map<String, String> imports = [:]
List<String> processedClasses = []
scan.getClassesWithAnnotation(ROUTE_FQN).each { ClassInfo ci ->
Expand All @@ -178,6 +228,25 @@ class ClassIntrospectionUtils {
imports
}

/**
* Find all CSSImports in project using whitelist
*
* @since 1.3
* @param scan
* the classpath scan with the annotations
* @return
* the values of the css imports
*/
static final Map<String,String> findCssImportsByWhitelist(ScanResult scan) {
Map<String, String> imports = [:]
scan.getClassesWithAnnotation(CSS_IMPORT_FQN).collect { ClassInfo ci ->
ci.getAnnotationInfoRepeatable(CSS_IMPORT_FQN).each { AnnotationInfo a ->
imports[a.parameterValues.value.value.toString()] = ci.name
}
}
imports
}

/**
* Find all classes which has an @Id filed annotation
*
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/vaadin-plugin.gdsl
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ contributor(vaadinCtx, {
property name: 'submitStatistics', type: Boolean.name
property name: 'baseTheme', type: String.name
property name: 'whitelistedPackages', type: String[].name
property name: 'scanStrategy', type: String.name
}
})

Expand All @@ -71,6 +72,7 @@ contributor(closureCtx, {
property name: 'submitStatistics', type: Boolean.name
property name: 'baseTheme', type: String.name
property name: 'whitelistedPackages', type: String[].name
property name: 'scanStrategy', type: String.name
}
if(enclosingCall(vaadinClientProperty)) {
property name: 'offlineCachePath', type: String.name
Expand Down

0 comments on commit 6ca2ffd

Please sign in to comment.