-
Notifications
You must be signed in to change notification settings - Fork 168
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Create Flow plugins for webpack (#9295)
Moved stats file handling to a custom plugin. Added feature for copying custom Flow plugins for use with webpack. Fixes #9283
- Loading branch information
Showing
9 changed files
with
615 additions
and
115 deletions.
There are no files selected for viewing
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,79 @@ | ||
## Flow Webpack plugins | ||
|
||
Flow now uses webpack plugins to make the `webpack.generated.js` cleaner and easier to extend | ||
without cluttering the file and making it long and complex. | ||
|
||
The files get installed with the task `TaskInstallWebpackPlugins` which reads the `webpack-plugins.json` | ||
in from `src/main/resources/plugins` and installs the plugins named here e.g. | ||
|
||
```json | ||
{ | ||
"plugins": [ | ||
"stats-plugin" | ||
] | ||
} | ||
``` | ||
|
||
The plugin itself should also be contained in `src/main/resources/plugins` with the | ||
folder name being the same as the plugin name. | ||
|
||
For stats-plugin this means it should be located in `src/main/resources/plugins/stats-plugin`. | ||
|
||
The plugin folder needs to contain the plugin javascript files plus a package.json with at least the fields | ||
`version`, `main`, `files` filled where: | ||
* `version` is the semver version for the plugin. | ||
(Plugin will not be updated if the same version already exists) | ||
* `main` depicts the main js file for the plugin. | ||
* `files` contains all files the plugin needs. | ||
(only these files will be copied) | ||
|
||
The full information would be preferred: | ||
|
||
```json | ||
{ | ||
"description": "stats-plugin", | ||
"keywords": [ | ||
"plugin" | ||
], | ||
"repository": "vaadin/flow", | ||
"name": "@vaadin/stats-plugin", | ||
"version": "1.0.0", | ||
"main": "stats-plugin.js", | ||
"author": "Vaadin Ltd", | ||
"license": "Apache-2.0", | ||
"bugs": { | ||
"url": "https://github.com/vaadin/flow/issues" | ||
}, | ||
"files": [ | ||
"stats-plugin.js" | ||
] | ||
} | ||
``` | ||
|
||
For creating a plugin see [Writing a plugin](https://webpack.js.org/contribute/writing-a-plugin/) | ||
|
||
## Using a Flow webpack plugin | ||
|
||
The flow plugins get installed to `node_modules/@vaadin` which means that using them we should use the for `@vaadin/${plugin-name}` | ||
|
||
As the plugins are meant for internal use the are added to `webpack.generated.js` and | ||
used from there. | ||
|
||
First we need to import the webpack plugin | ||
|
||
```js | ||
const StatsPlugin = require('@vaadin/stats-plugin'); | ||
``` | ||
|
||
then add the plugin with required options to `plugins` in the generated file | ||
```js | ||
plugins: [ | ||
new StatsPlugin({ | ||
devMode: devMode, | ||
statsFile: statsFile, | ||
setResults: function (statsFile) { | ||
stats = statsFile; | ||
} | ||
}), | ||
] | ||
``` |
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
179 changes: 179 additions & 0 deletions
179
flow-server/src/main/java/com/vaadin/flow/server/frontend/TaskInstallWebpackPlugins.java
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,179 @@ | ||
/* | ||
* Copyright 2000-2020 Vaadin Ltd. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
* use this file except in compliance with the License. You may obtain a copy of | ||
* the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
* License for the specific language governing permissions and limitations under | ||
* the License. | ||
*/ | ||
package com.vaadin.flow.server.frontend; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.UncheckedIOException; | ||
import java.net.URL; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
import org.apache.commons.io.FileUtils; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import elemental.json.Json; | ||
import elemental.json.JsonArray; | ||
import elemental.json.JsonObject; | ||
import static com.vaadin.flow.server.Constants.PACKAGE_JSON; | ||
import static java.nio.charset.StandardCharsets.UTF_8; | ||
|
||
/** | ||
* Task that installs any Flow webpack plugins into node_modules/@vaadin for | ||
* use with webpack compilation. | ||
* <p> | ||
* This should preferably be executed after npm installation to not make it skip | ||
* or have the plugins deleted by {@link TaskRunNpmInstall}. | ||
* | ||
* @since | ||
*/ | ||
public class TaskInstallWebpackPlugins implements FallibleCommand { | ||
|
||
private File nodeModulesFolder; | ||
|
||
/** | ||
* Copy Flow webpack plugins into the given nodeModulesFolder. | ||
* | ||
* @param nodeModulesFolder | ||
* node_modules folder to copy files to | ||
*/ | ||
public TaskInstallWebpackPlugins(File nodeModulesFolder) { | ||
this.nodeModulesFolder = nodeModulesFolder; | ||
} | ||
|
||
@Override | ||
public void execute() { | ||
getPlugins().forEach(plugin -> { | ||
try { | ||
generatePluginFiles(plugin); | ||
} catch (IOException ioe) { | ||
throw new UncheckedIOException( | ||
"Installation of Flow webpack plugin '" + plugin | ||
+ "' failed", ioe); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Get names for plugins to install into node_modules. | ||
* | ||
* @return names of plugins to install | ||
*/ | ||
protected List<String> getPlugins() { | ||
try { | ||
final JsonObject jsonFile = getJsonFile( | ||
"plugins/webpack-plugins.json"); | ||
if (jsonFile == null) { | ||
log().error( | ||
"Couldn't locate plugins/webpack-plugins.json, no Webpack plugins for Flow will be installed." | ||
+ "If webpack build fails validate flow-server jar content."); | ||
return Collections.emptyList(); | ||
} | ||
|
||
final JsonArray plugins = jsonFile.getArray("plugins"); | ||
List<String> pluginsToInstall = new ArrayList<>(plugins.length()); | ||
for (int i = 0; i < plugins.length(); i++) { | ||
pluginsToInstall.add(plugins.getString(i)); | ||
} | ||
return pluginsToInstall; | ||
} catch (IOException ioe) { | ||
throw new UncheckedIOException( | ||
"Couldn't load webpack-plugins.json file", ioe); | ||
} | ||
} | ||
|
||
private void generatePluginFiles(String pluginName) throws IOException { | ||
// Get the target folder where the plugin should be installed to | ||
File pluginTargetFile = new File(nodeModulesFolder, | ||
"@vaadin/" + pluginName); | ||
|
||
final String pluginFolderName = "plugins/" + pluginName + "/"; | ||
final JsonObject packageJson = getJsonFile( | ||
pluginFolderName + PACKAGE_JSON); | ||
if (packageJson == null) { | ||
log().error( | ||
"Couldn't locate '{}' for plugin '{}'. Plugin will not be installed.", | ||
PACKAGE_JSON, pluginName); | ||
return; | ||
} | ||
|
||
// Validate installed version and don't override if same | ||
if (pluginTargetFile.exists() && new File(pluginTargetFile, | ||
PACKAGE_JSON).exists()) { | ||
String packageFile = FileUtils | ||
.readFileToString(new File(pluginTargetFile, PACKAGE_JSON), | ||
StandardCharsets.UTF_8); | ||
final FrontendVersion packageVersion = new FrontendVersion( | ||
Json.parse(packageFile).getString("version")); | ||
FrontendVersion pluginVersion = new FrontendVersion( | ||
packageJson.getString("version")); | ||
if (packageVersion.isEqualTo(pluginVersion)) { | ||
log().debug( | ||
"Skipping install of {} for version {} already installed", | ||
pluginName, pluginVersion.getFullVersion()); | ||
return; | ||
} | ||
} | ||
|
||
// Create target folder if necessary | ||
FileUtils.forceMkdir(pluginTargetFile); | ||
|
||
// copy only files named in package.json { files } | ||
final JsonArray files = packageJson.getArray("files"); | ||
for (int i = 0; i < files.length(); i++) { | ||
final String file = files.getString(i); | ||
FileUtils.copyURLToFile(getResourceUrl(pluginFolderName + file), | ||
new File(pluginTargetFile, file)); | ||
} | ||
// copy package.json to plugin directory | ||
FileUtils.copyURLToFile(getResourceUrl(pluginFolderName + PACKAGE_JSON), | ||
new File(pluginTargetFile, PACKAGE_JSON)); | ||
} | ||
|
||
private JsonObject getJsonFile(String jsonFilePath) throws IOException { | ||
final URL urlResource = getResourceUrl(jsonFilePath); | ||
if (urlResource == null) { | ||
return null; | ||
} | ||
File jsonFile = new File(urlResource.getFile()); | ||
String jsonString; | ||
if (!jsonFile.exists()) { | ||
try (InputStream resourceAsStream = this.getClass().getClassLoader() | ||
.getResourceAsStream(jsonFilePath)) { | ||
if (resourceAsStream != null) { | ||
jsonString = FrontendUtils.streamToString(resourceAsStream); | ||
} else { | ||
return null; | ||
} | ||
} | ||
} else { | ||
jsonString = FileUtils.readFileToString(jsonFile, UTF_8); | ||
} | ||
return Json.parse(jsonString); | ||
} | ||
|
||
private URL getResourceUrl(String resource) { | ||
return this.getClass().getClassLoader().getResource(resource); | ||
} | ||
|
||
private Logger log() { | ||
return LoggerFactory.getLogger(this.getClass()); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
flow-server/src/main/resources/plugins/stats-plugin/package.json
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,18 @@ | ||
{ | ||
"description": "stats-plugin", | ||
"keywords": [ | ||
"plugin" | ||
], | ||
"repository": "vaadin/flow", | ||
"name": "@vaadin/stats-plugin", | ||
"version": "1.0.0", | ||
"main": "stats-plugin.js", | ||
"author": "Vaadin Ltd", | ||
"license": "Apache-2.0", | ||
"bugs": { | ||
"url": "https://github.com/vaadin/flow/issues" | ||
}, | ||
"files": [ | ||
"stats-plugin.js" | ||
] | ||
} |
Oops, something went wrong.