@studyportals/webpack-helper 6.0.7-9 → 6.1.1-1
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 +52 -0
- package/package.json +4 -4
- package/src/factories/chunks-retry-plugin-factory.js +21 -17
- package/src/model/configuration.js +5 -1
- package/src/plugins/retry-chunk-load.plugin.js +81 -0
- package/src/presets/javascript.preset.js +1 -3
- package/src/presets/typescript.preset.js +0 -2
- package/src/presets/vue-typescript-no-code-splitting.preset.js +20 -0
- package/src/presets/vue-typescript.preset.js +1 -3
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@ WebpackHelper is a utility that makes it easy to set up a webpack configuration
|
|
|
6
6
|
- [Webpack version](#webpack-version)
|
|
7
7
|
- [Notes on updating Webpack from v4 to v5](#notes-on-updating-webpack-from-v4-to-v5)
|
|
8
8
|
- [Quick Start](#quick-start)
|
|
9
|
+
- [Chunks Retry](#chunks-retry)
|
|
10
|
+
- [Defaults](#defaults)
|
|
11
|
+
- [Configuration](#configuration)
|
|
12
|
+
- [Limitations](#limitations)
|
|
9
13
|
- [Configuration](#configuration)
|
|
10
14
|
- [entry](#entry-required)
|
|
11
15
|
- [output](#output-required)
|
|
@@ -123,6 +127,54 @@ When your configuration is created you can get an instance of it for the correct
|
|
|
123
127
|
```javascript
|
|
124
128
|
module.exports = (env, details) => configuration.get(details.mode);
|
|
125
129
|
```
|
|
130
|
+
|
|
131
|
+
## Chunks Retry
|
|
132
|
+
Webpack Helpers has a default configuration to retry chunks which cannot get loaded on the first try (for whatever reason, network or otherwise).
|
|
133
|
+
|
|
134
|
+
It has an Exponential Backoff to calculate the delay between retries of a maximum of `2` seconds.
|
|
135
|
+
|
|
136
|
+
The functionality is implemented thru a plugin called `webpack-retry-chunk-load-plugin` https://www.npmjs.com/package/webpack-retry-chunk-load-plugin.
|
|
137
|
+
|
|
138
|
+
### Defaults
|
|
139
|
+
|
|
140
|
+
By default it is enabled if the selected preset is `vue-typescript` and it uses `6` as the number of retries it does and it uses the `rollbar` instance on the `window` object (Portal instance) to log an error when it reaches the threshold of retries without being able to load the chunk (`new Error('Failed to load chunk for after retries')`).
|
|
141
|
+
|
|
142
|
+
### Configuration
|
|
143
|
+
To configure the functionality you can provide an object following this interface
|
|
144
|
+
```js
|
|
145
|
+
interface IChunksRetryPluginConfig {
|
|
146
|
+
inject: boolean; //if you want the plugin injected
|
|
147
|
+
|
|
148
|
+
threshold: number; //amount of retries
|
|
149
|
+
|
|
150
|
+
thresholdReachedCallBack?: string; //a function (body of it as string) to be called when the amount of retries has been exhausted
|
|
151
|
+
|
|
152
|
+
rollbarInstanceIdentifier?: string; //a key of a Rollbar instance present on the 'window' object e.g if you want to use window['rollbar_PQ'] to track the error in the Profile Questionnaire project in Rollbar then you send 'rollbar_PQ'
|
|
153
|
+
|
|
154
|
+
disableDefaultRollbarError?: boolean; //if you don't want to log by default when the threshold has been reached
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
and you can provide this thru the `webpack` config of your service like so
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
const configuration = WebpackHelper.create({
|
|
162
|
+
chunksRetryConfig: {
|
|
163
|
+
threshold: 10,
|
|
164
|
+
thresholdReachedCallBack: `console.log("hello")`,
|
|
165
|
+
},
|
|
166
|
+
...
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Limitations
|
|
170
|
+
We have some limitations in this implementation based on the plugin we used and that the code will be part of the webpack exported code
|
|
171
|
+
|
|
172
|
+
### Only use ES5
|
|
173
|
+
You can only write ES5 code inside the `thresholdReachedCallBack`
|
|
174
|
+
|
|
175
|
+
### String as function
|
|
176
|
+
The function `thresholdReachedCallBack` must be a string (aka the body of the function) and we cannot change that to be a function while retaining the simplicity of the code.
|
|
177
|
+
|
|
126
178
|
## Configuration
|
|
127
179
|
|
|
128
180
|
Webpack Helper offers a compact but flexible configuration interface. Below you will find every option available.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@studyportals/webpack-helper",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.1.1-1",
|
|
4
4
|
"description": "A collection of standard configurations and helpers for webpack.",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"scripts": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"@studyportals/bob-manifest-generator": "^5.0.1",
|
|
24
24
|
"@studyportals/browserslist-config": "^1.2.4",
|
|
25
25
|
"@studyportals/dll-availability-tracker": "^2.0.0",
|
|
26
|
-
"@studyportals/vue-config": "^5.0.
|
|
26
|
+
"@studyportals/vue-config": "^5.0.3-0",
|
|
27
27
|
"css-loader": "^6.10.0",
|
|
28
28
|
"css-minimizer-webpack-plugin": "^6.0.0",
|
|
29
29
|
"esbuild-loader": "^4.0.3",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
|
33
33
|
"mini-css-extract-plugin": "^2.8.0",
|
|
34
34
|
"path": "^0.12.7",
|
|
35
|
+
"prettier": "^2.8.8",
|
|
35
36
|
"sass": "^1.71.1",
|
|
36
37
|
"sass-loader": "^14.1.1",
|
|
37
38
|
"terser-webpack-plugin": "^5.3.10",
|
|
@@ -39,8 +40,7 @@
|
|
|
39
40
|
"vue-loader": "^17.4.2",
|
|
40
41
|
"webpack": "^5.90.3",
|
|
41
42
|
"webpack-cli": "^5.1.4",
|
|
42
|
-
"webpack-merge": "^5.10.0"
|
|
43
|
-
"webpack-retry-chunk-load-plugin": "^3.1.1"
|
|
43
|
+
"webpack-merge": "^5.10.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^20.11.20",
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
3
|
+
const mode_1 = require("../enums/mode");
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const process = require('process');
|
|
6
|
+
const { RetryChunkLoadPlugin } = require(path.join(process.cwd(), 'node_modules', '@studyportals/webpack-helper', 'src', 'plugins', 'retry-chunk-load.plugin.js'));
|
|
4
7
|
class ChunksRetryPluginFactory {
|
|
5
|
-
static
|
|
6
|
-
if (!preset.retryChunksConfig)
|
|
7
|
-
return;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
static IsPresetSupported(preset) {
|
|
9
|
+
if (!preset.retryChunksConfig)
|
|
10
|
+
return false;
|
|
11
|
+
if (preset.mode === mode_1.Mode.DEVELOPMENT)
|
|
12
|
+
return false;
|
|
13
|
+
if (!preset.retryChunksConfig.inject)
|
|
14
|
+
return false;
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
static GetRetryChunksPlugin(preset) {
|
|
18
|
+
return ChunksRetryPluginFactory.CustomRetryChunksPlugin(preset);
|
|
16
19
|
}
|
|
17
20
|
static CustomRetryChunksPlugin(preset) {
|
|
18
21
|
const { retryChunksConfig } = preset;
|
|
@@ -32,17 +35,18 @@ class ChunksRetryPluginFactory {
|
|
|
32
35
|
const { retryChunksConfig } = preset;
|
|
33
36
|
let lastResortScript = ``;
|
|
34
37
|
if (retryChunksConfig.thresholdReachedCallBack) {
|
|
35
|
-
lastResortScript
|
|
36
|
-
${retryChunksConfig.thresholdReachedCallBack
|
|
38
|
+
lastResortScript += `
|
|
39
|
+
${retryChunksConfig.thresholdReachedCallBack}
|
|
37
40
|
`;
|
|
38
41
|
}
|
|
39
42
|
if (!retryChunksConfig.disableDefaultRollbarError) {
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
const rollbarInstanceIdentifier = retryChunksConfig.rollbarInstanceIdentifier || 'rollbar';
|
|
44
|
+
lastResortScript += `
|
|
45
|
+
if (!window || !window['${rollbarInstanceIdentifier}'] || !window['${rollbarInstanceIdentifier}'].error) {
|
|
42
46
|
return;
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
window['
|
|
49
|
+
window['${rollbarInstanceIdentifier}'].error(new Error('Failed to load chunk for after retries'));
|
|
46
50
|
`;
|
|
47
51
|
}
|
|
48
52
|
return lastResortScript;
|
|
@@ -41,6 +41,7 @@ class Configuration {
|
|
|
41
41
|
this.fileName = options.output.fileName || "[name]";
|
|
42
42
|
this.chunksRetryConfig = options.chunksRetryConfig || {
|
|
43
43
|
threshold: DEFAULT_CHUNKS_RETRY_CONFIG_THRESHOLD,
|
|
44
|
+
rollbarInstanceIdentifier: 'rollbar',
|
|
44
45
|
inject: this.preset === preset_1.Preset.VUE_TYPESCRIPT
|
|
45
46
|
};
|
|
46
47
|
}
|
|
@@ -79,8 +80,11 @@ class Configuration {
|
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
injectPreset(preset) {
|
|
82
|
-
chunks_retry_plugin_factory_1.default.InjectPlugin(preset);
|
|
83
83
|
const plugins = preset.getPlugins(this.getCSSFileName());
|
|
84
|
+
if (chunks_retry_plugin_factory_1.default.IsPresetSupported(preset)) {
|
|
85
|
+
const retryPlugin = chunks_retry_plugin_factory_1.default.GetRetryChunksPlugin(preset);
|
|
86
|
+
plugins.push(retryPlugin);
|
|
87
|
+
}
|
|
84
88
|
const module = preset.getModule();
|
|
85
89
|
plugins.forEach(plugin => {
|
|
86
90
|
this.plugins.addPlugin(plugin);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RetryChunkLoadPlugin = void 0;
|
|
4
|
+
const prettier = require('prettier');
|
|
5
|
+
const { RuntimeGlobals } = require('webpack');
|
|
6
|
+
const pluginName = 'RetryChunkLoadPlugin';
|
|
7
|
+
class RetryChunkLoadPlugin {
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.options = Object.assign({}, options);
|
|
10
|
+
}
|
|
11
|
+
apply(compiler) {
|
|
12
|
+
compiler.hooks.thisCompilation.tap(pluginName, compilation => {
|
|
13
|
+
const { mainTemplate, runtimeTemplate } = compilation;
|
|
14
|
+
const maxRetryValueFromOptions = Number(this.options.maxRetries);
|
|
15
|
+
const maxRetries = Number.isInteger(maxRetryValueFromOptions) &&
|
|
16
|
+
maxRetryValueFromOptions > 0
|
|
17
|
+
? maxRetryValueFromOptions
|
|
18
|
+
: 1;
|
|
19
|
+
const getCacheBustString = () => this.options.cacheBust
|
|
20
|
+
? `
|
|
21
|
+
(${this.options.cacheBust})();
|
|
22
|
+
`
|
|
23
|
+
: '"cache-bust=true"';
|
|
24
|
+
mainTemplate.hooks.localVars.tap({ name: pluginName, stage: 1 }, (source, chunk) => {
|
|
25
|
+
const currentChunkName = chunk.name;
|
|
26
|
+
const addRetryCode = !this.options.chunks ||
|
|
27
|
+
this.options.chunks.some(chunk => chunk === currentChunkName);
|
|
28
|
+
const getRetryDelay = typeof this.options.retryDelay === 'string'
|
|
29
|
+
? this.options.retryDelay
|
|
30
|
+
: `function() { return ${this.options.retryDelay || 0} }`;
|
|
31
|
+
if (!addRetryCode)
|
|
32
|
+
return source;
|
|
33
|
+
const script = runtimeTemplate.iife('', `
|
|
34
|
+
if(typeof ${RuntimeGlobals.require} !== "undefined") {
|
|
35
|
+
var oldGetScript = ${RuntimeGlobals.getChunkScriptFilename};
|
|
36
|
+
var oldLoadScript = ${RuntimeGlobals.ensureChunk};
|
|
37
|
+
var queryMap = {};
|
|
38
|
+
var countMap = {};
|
|
39
|
+
var getRetryDelay = ${getRetryDelay}
|
|
40
|
+
${RuntimeGlobals.getChunkScriptFilename} = function(chunkId){
|
|
41
|
+
var result = oldGetScript(chunkId);
|
|
42
|
+
return result + (queryMap.hasOwnProperty(chunkId) ? '?' + queryMap[chunkId] : '');
|
|
43
|
+
};
|
|
44
|
+
${RuntimeGlobals.ensureChunk} = function(chunkId){
|
|
45
|
+
var result = oldLoadScript(chunkId);
|
|
46
|
+
return result.catch(function(error){
|
|
47
|
+
var retries = countMap.hasOwnProperty(chunkId) ? countMap[chunkId] : ${maxRetries};
|
|
48
|
+
if (retries < 1) {
|
|
49
|
+
var realSrc = oldGetScript(chunkId);
|
|
50
|
+
error.message = 'Loading chunk ' + chunkId + ' failed after ${maxRetries} retries.\\n(' + realSrc + ')';
|
|
51
|
+
error.request = realSrc;${this.options.lastResortScript
|
|
52
|
+
? this.options.lastResortScript
|
|
53
|
+
: ''}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
return new Promise(function (resolve) {
|
|
57
|
+
var retryAttempt = ${maxRetries} - retries + 1;
|
|
58
|
+
setTimeout(function () {
|
|
59
|
+
var retryAttemptString = '&retry-attempt=' + retryAttempt;
|
|
60
|
+
var cacheBust = ${getCacheBustString()} + retryAttemptString;
|
|
61
|
+
queryMap[chunkId] = cacheBust;
|
|
62
|
+
countMap[chunkId] = retries - 1;
|
|
63
|
+
resolve(${RuntimeGlobals.ensureChunk}(chunkId));
|
|
64
|
+
}, getRetryDelay(retryAttempt))
|
|
65
|
+
})
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
}`);
|
|
69
|
+
return (source +
|
|
70
|
+
prettier.format(script, {
|
|
71
|
+
singleQuote: true,
|
|
72
|
+
parser: 'babel',
|
|
73
|
+
bracketSpacing: true,
|
|
74
|
+
trailingComma: 'all',
|
|
75
|
+
arrowParens: 'avoid',
|
|
76
|
+
}));
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.RetryChunkLoadPlugin = RetryChunkLoadPlugin;
|
|
@@ -7,14 +7,12 @@ class JavascriptPreset {
|
|
|
7
7
|
constructor(mode, retryChunksConfig) {
|
|
8
8
|
this.mode = mode;
|
|
9
9
|
this.retryChunksConfig = retryChunksConfig;
|
|
10
|
-
this.injectedPlugins = [];
|
|
11
10
|
}
|
|
12
11
|
getPlugins(filename) {
|
|
13
12
|
return [
|
|
14
13
|
new MiniCssExtractPlugin({
|
|
15
14
|
filename
|
|
16
|
-
})
|
|
17
|
-
...this.injectedPlugins
|
|
15
|
+
})
|
|
18
16
|
];
|
|
19
17
|
}
|
|
20
18
|
getModule() {
|
|
@@ -7,14 +7,12 @@ class TypeScriptPreset {
|
|
|
7
7
|
constructor(mode, retryChunksConfig) {
|
|
8
8
|
this.mode = mode;
|
|
9
9
|
this.retryChunksConfig = retryChunksConfig;
|
|
10
|
-
this.injectedPlugins = [];
|
|
11
10
|
}
|
|
12
11
|
getPlugins(filename) {
|
|
13
12
|
return [
|
|
14
13
|
new MiniCssExtractPlugin({
|
|
15
14
|
filename
|
|
16
15
|
}),
|
|
17
|
-
...this.injectedPlugins
|
|
18
16
|
];
|
|
19
17
|
}
|
|
20
18
|
getModule() {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VueTypescriptNoCodeSplittingPreset = void 0;
|
|
4
|
+
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
|
5
|
+
const VueLoaderPlugin = require('vue-loader').VueLoaderPlugin;
|
|
6
|
+
const vue_typescript_preset_1 = require("./vue-typescript.preset");
|
|
7
|
+
class VueTypescriptNoCodeSplittingPreset extends vue_typescript_preset_1.VueTypeScriptPreset {
|
|
8
|
+
constructor(mode) {
|
|
9
|
+
super(mode);
|
|
10
|
+
}
|
|
11
|
+
getPlugins(filename) {
|
|
12
|
+
return [
|
|
13
|
+
new MiniCssExtractPlugin({
|
|
14
|
+
filename
|
|
15
|
+
}),
|
|
16
|
+
new VueLoaderPlugin()
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.VueTypescriptNoCodeSplittingPreset = VueTypescriptNoCodeSplittingPreset;
|
|
@@ -12,7 +12,6 @@ class VueTypeScriptPreset {
|
|
|
12
12
|
this.mode = mode;
|
|
13
13
|
this.retryChunksConfig = retryChunksConfig;
|
|
14
14
|
this.customElementTags = customElementTags;
|
|
15
|
-
this.injectedPlugins = [];
|
|
16
15
|
}
|
|
17
16
|
getPlugins(filename) {
|
|
18
17
|
return [
|
|
@@ -24,8 +23,7 @@ class VueTypeScriptPreset {
|
|
|
24
23
|
filename,
|
|
25
24
|
ignoreOrder: true
|
|
26
25
|
}),
|
|
27
|
-
new VueLoaderPlugin()
|
|
28
|
-
...this.injectedPlugins
|
|
26
|
+
new VueLoaderPlugin()
|
|
29
27
|
];
|
|
30
28
|
}
|
|
31
29
|
getModule() {
|