@redhat-cloud-services/frontend-components-config-utilities 1.5.6 → 1.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/extension-mapper.js +77 -0
- package/extensions-plugin.js +54 -0
- package/federated-modules.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -123,3 +123,71 @@ This packages exposes these federated shared dependencies
|
|
|
123
123
|
* `@redhat-cloud-services/frontend-components` - version taken from your `package.json`
|
|
124
124
|
* `@redhat-cloud-services/frontend-components-utilities` - version taken from your `package.json`
|
|
125
125
|
* `@redhat-cloud-services/frontend-components-notifications` - version taken from your `package.json`
|
|
126
|
+
|
|
127
|
+
## Extensions plugin
|
|
128
|
+
|
|
129
|
+
In order to share some code into extension points or to add new extension point we can use `ExtensionsPlugin`
|
|
130
|
+
|
|
131
|
+
Simply import it in your webpack config and add it to your plugins
|
|
132
|
+
|
|
133
|
+
```JS
|
|
134
|
+
const { resolve } = require('path');
|
|
135
|
+
const config = require('@redhat-cloud-services/frontend-components-config');
|
|
136
|
+
const ExtensionsPlugin = require('@redhat-cloud-services/frontend-components-config/extensions-plugin');
|
|
137
|
+
|
|
138
|
+
const { config: webpackConfig, plugins } = config({
|
|
139
|
+
rootFolder: resolve(__dirname, '../'),
|
|
140
|
+
...(process.env.BETA && { deployment: 'beta/apps' }),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
plugins.push(
|
|
144
|
+
require('@redhat-cloud-services/frontend-components-config/federated-modules')({
|
|
145
|
+
root: resolve(__dirname, '../'),
|
|
146
|
+
}),
|
|
147
|
+
new ExtensionsPlugin({})
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
module.exports = {
|
|
151
|
+
...webpackConfig,
|
|
152
|
+
plugins
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Arguments
|
|
157
|
+
|
|
158
|
+
There are three arguments `ExtensionsPlugin` constructor accepts:
|
|
159
|
+
* `pluginConfig`
|
|
160
|
+
* `fedModuleConfig`
|
|
161
|
+
* `options`
|
|
162
|
+
|
|
163
|
+
### `pluginConfig`
|
|
164
|
+
|
|
165
|
+
This config contains information about extensions, plugin requirements, its name and description. Most of it (name, description and version) is calculated from your root `package.json`. But you can override these values:
|
|
166
|
+
|
|
167
|
+
* `name` - plugin name (pulled from `package.json`)
|
|
168
|
+
* `version` - version of the plugin
|
|
169
|
+
* `displayName` - display name of the plugin
|
|
170
|
+
* `description` - description of the plugin (pulled from `package.json`)
|
|
171
|
+
* `dependencies` - object of dependencies which will be passed down to module federation (no need to list general react dependencies)
|
|
172
|
+
* `disableStaticPlugins` - list of static plugins this plugin disables on load
|
|
173
|
+
* `extensions` - list of extension objects.
|
|
174
|
+
|
|
175
|
+
#### extension object
|
|
176
|
+
|
|
177
|
+
Each extension object requires a `type` and `properties`. The type can be either custom extension or one of predefined:
|
|
178
|
+
|
|
179
|
+
* `console.navigation/section` - a section in navigation (identifies secondary nav)
|
|
180
|
+
* `properties`
|
|
181
|
+
* `id` - id of the section
|
|
182
|
+
* `name` - name of the section, this will be shown in the UI
|
|
183
|
+
* `console.page/route` - route passed to react-router
|
|
184
|
+
* `properties` - in theory any react-router path prop can be used here
|
|
185
|
+
* `path` - (string, or array) path on which the component will be rendered
|
|
186
|
+
* `component`
|
|
187
|
+
* `$codeRef` - federated module used to render on the route
|
|
188
|
+
* `console.navigation/href` - navigation href, used to render leafs of navigation
|
|
189
|
+
* `properties`
|
|
190
|
+
* `id` - id of the href
|
|
191
|
+
* `section` - (optional) used to group nav items under section (omit for flat nav)
|
|
192
|
+
* `name` - name of the href, thiw will be shown in the UI
|
|
193
|
+
* `href` - used to mutate the URL
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const webpack = require('webpack');
|
|
2
|
+
|
|
3
|
+
class ExtensionsMapper {
|
|
4
|
+
constructor(plugin, options) {
|
|
5
|
+
if (!plugin) {
|
|
6
|
+
throw new Error('Missing plugin config!');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
this.plugin = plugin;
|
|
10
|
+
this.options = {
|
|
11
|
+
remoteEntryCallback: 'window.loadPluginEntry',
|
|
12
|
+
remoteEntryFile: 'plugin-entry.js',
|
|
13
|
+
pluginManifestFile: 'plugin-manifest.json',
|
|
14
|
+
...options
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
apply(compiler) {
|
|
19
|
+
compiler.hooks.thisCompilation.tap(ExtensionsMapper.name, (compilation) => {
|
|
20
|
+
// Generate extensions manifest
|
|
21
|
+
compilation.hooks.processAssets.tap(
|
|
22
|
+
{
|
|
23
|
+
name: ExtensionsMapper.name,
|
|
24
|
+
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
|
|
25
|
+
},
|
|
26
|
+
() => {
|
|
27
|
+
compilation.emitAsset(
|
|
28
|
+
this.options.pluginManifestFile,
|
|
29
|
+
new webpack.sources.RawSource(
|
|
30
|
+
Buffer.from(JSON.stringify({
|
|
31
|
+
version: '0.0.0',
|
|
32
|
+
description: '',
|
|
33
|
+
displayName: '',
|
|
34
|
+
dependencies: {},
|
|
35
|
+
disableStaticPlugins: {},
|
|
36
|
+
...this.plugin
|
|
37
|
+
}, null, 2))
|
|
38
|
+
)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Update plugin-entry.js file
|
|
44
|
+
compilation.hooks.processAssets.tap(
|
|
45
|
+
{
|
|
46
|
+
name: ExtensionsMapper.name,
|
|
47
|
+
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
|
|
48
|
+
},
|
|
49
|
+
() => {
|
|
50
|
+
compilation.updateAsset(this.options.remoteEntryFile, (source) => {
|
|
51
|
+
const newSource = new webpack.sources.ReplaceSource(source);
|
|
52
|
+
|
|
53
|
+
const fromIndex = source
|
|
54
|
+
.source()
|
|
55
|
+
.toString()
|
|
56
|
+
.indexOf(`${this.options.remoteEntryCallback}(`);
|
|
57
|
+
|
|
58
|
+
if (fromIndex < 0) {
|
|
59
|
+
const error = new webpack.WebpackError(`Missing call to ${this.options.remoteEntryCallback}`);
|
|
60
|
+
error.file = this.options.remoteEntryFile;
|
|
61
|
+
compilation.errors.push(error);
|
|
62
|
+
} else {
|
|
63
|
+
newSource.insert(
|
|
64
|
+
fromIndex + this.options.remoteEntryCallback.length + 1,
|
|
65
|
+
`'${this.plugin.name}@${this.plugin.version}', `
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return newSource;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = ExtensionsMapper;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const { resolve } = require('path');
|
|
2
|
+
const fedModule = require('./federated-modules');
|
|
3
|
+
const ExtensionsMapper = require('./extension-mapper');
|
|
4
|
+
|
|
5
|
+
class ExtensionsPlugin {
|
|
6
|
+
constructor(plugin, fedMod, options) {
|
|
7
|
+
if (!plugin) {
|
|
8
|
+
throw new Error('Missing plugin config!');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.plugin = plugin;
|
|
12
|
+
this.fedMod = {
|
|
13
|
+
moduleName: 'plugin-entry',
|
|
14
|
+
libName: 'window.loadPluginEntry',
|
|
15
|
+
useFileHash: false,
|
|
16
|
+
libType: 'jsonp',
|
|
17
|
+
...fedMod
|
|
18
|
+
};
|
|
19
|
+
this.options = {
|
|
20
|
+
remoteEntryCallback: 'window.loadPluginEntry',
|
|
21
|
+
remoteEntryFile: `${this.fedMod.moduleName}.js`,
|
|
22
|
+
pluginManifestFile: 'plugin-manifest.json',
|
|
23
|
+
...options
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
apply(compiler) {
|
|
28
|
+
const root = this.fedMod.root || compiler.context;
|
|
29
|
+
const { insights, version, description } = require(resolve(root, './package.json')) || {};
|
|
30
|
+
|
|
31
|
+
// create federation module
|
|
32
|
+
fedModule({
|
|
33
|
+
...this.fedMod,
|
|
34
|
+
root
|
|
35
|
+
}).apply(compiler);
|
|
36
|
+
|
|
37
|
+
// generate plugin manifest and update plugin-entry file
|
|
38
|
+
new ExtensionsMapper(
|
|
39
|
+
{
|
|
40
|
+
...this.plugin,
|
|
41
|
+
name: this.plugin.name || (insights && insights.appname),
|
|
42
|
+
version: this.plugin.version || version || '0.0.0',
|
|
43
|
+
displayName: this.plugin.displayName || '',
|
|
44
|
+
description: this.plugin.description || description || '',
|
|
45
|
+
dependencies: { ...this.plugin.dependencies || {} },
|
|
46
|
+
disableStaticPlugins: [ ...this.plugin.disableStaticPlugins || [] ],
|
|
47
|
+
extensions: [ ...this.plugin.extensions || [] ]
|
|
48
|
+
},
|
|
49
|
+
this.options
|
|
50
|
+
).apply(compiler);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = ExtensionsPlugin;
|
package/federated-modules.js
CHANGED
|
@@ -27,6 +27,8 @@ module.exports = ({
|
|
|
27
27
|
debug,
|
|
28
28
|
moduleName,
|
|
29
29
|
useFileHash = true,
|
|
30
|
+
libType = 'var',
|
|
31
|
+
libName,
|
|
30
32
|
exclude = []
|
|
31
33
|
}) => {
|
|
32
34
|
const { dependencies, insights } = require(resolve(root, './package.json')) || {};
|
|
@@ -64,7 +66,7 @@ module.exports = ({
|
|
|
64
66
|
return new ModuleFederationPlugin({
|
|
65
67
|
name: appName,
|
|
66
68
|
filename: `${appName}${useFileHash ? '.[chunkhash]' : ''}.js`,
|
|
67
|
-
library: { type:
|
|
69
|
+
library: { type: libType, name: libName || appName },
|
|
68
70
|
exposes: {
|
|
69
71
|
...exposes || {
|
|
70
72
|
'./RootApp': resolve(root, './src/AppEntry')
|
package/package.json
CHANGED