@oroinc/oro-webpack-config-builder 5.1.0-alpha3 → 5.1.0-alpha30

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/package.json CHANGED
@@ -1,46 +1,49 @@
1
1
  {
2
2
  "name": "@oroinc/oro-webpack-config-builder",
3
- "version": "5.1.0-alpha3",
3
+ "version": "5.1.0-alpha30",
4
4
  "author": "Oro, Inc (https://www.oroinc.com)",
5
5
  "license": "MIT",
6
6
  "description": "An integration of OroPlatform based applications with the Webpack.",
7
7
  "main": "oro-webpack-config.js",
8
8
  "dependencies": {
9
- "@babel/core": "^7.10.4",
10
- "@babel/plugin-transform-runtime": "^7.10.4",
11
- "@babel/preset-env": "^7.10.4",
12
- "autoprefixer": "^9.8.6",
13
- "babel-loader": "^8.1.0",
14
- "css-loader": "^5.0.2",
15
- "css-minimizer-webpack-plugin": "^1.2.0",
9
+ "@babel/core": "^7.16.0",
10
+ "@babel/plugin-transform-runtime": "^7.16.0",
11
+ "@babel/preset-env": "^7.16.0",
12
+ "autoprefixer": "^10.4.0",
13
+ "babel-loader": "^8.2.3",
14
+ "bindings": "^1.5.0",
15
+ "css-loader": "^6.5.1",
16
+ "css-minimizer-webpack-plugin": "^3.1.3",
16
17
  "deepmerge": "^4.2.2",
17
- "exports-loader": "^2.0.0",
18
- "expose-loader": "^2.0.0",
18
+ "exports-loader": "^3.1.0",
19
+ "expose-loader": "^3.1.0",
19
20
  "extract-loader": "^5.1.0",
20
21
  "file-loader": "^6.2.0",
21
22
  "happypack": "^5.0.1",
22
- "html-webpack-plugin": "^5.2.0",
23
- "imports-loader": "^2.0.0",
24
- "js-yaml": "^4.0.0",
25
- "mini-css-extract-plugin": "^1.3.8",
23
+ "html-webpack-plugin": "^5.5.0",
24
+ "imports-loader": "^3.1.1",
25
+ "js-yaml": "^4.1.0",
26
+ "mini-css-extract-plugin": "^2.4.4",
26
27
  "minimist": "^1.2.3",
28
+ "nan": "^2.15.0",
27
29
  "path": "0.12.7",
28
- "postcss": "^8.1.0",
29
- "postcss-loader": "^5.0.0",
30
+ "postcss": "^8.3.11",
31
+ "postcss-loader": "^6.2.0",
30
32
  "printf": "^0.6.0",
33
+ "resolve-url-loader": "^4.0.0",
31
34
  "rtlcss-webpack-plugin": "^4.0.6",
32
- "sass": "^1.32.8",
33
- "sass-loader": "^11.0.1",
34
- "style-loader": "^2.0.0",
35
- "terser": "^5.6.0",
35
+ "sass": "^1.43.4",
36
+ "sass-loader": "^12.3.0",
37
+ "style-loader": "^3.3.1",
38
+ "terser": "^5.9.0",
36
39
  "text-loader": "0.0.1",
37
- "underscore": "^1.10.2",
40
+ "underscore": "^1.13.1",
38
41
  "url-loader": "^4.1.1",
39
- "webpack": "^5.23.0",
40
- "webpack-bundle-analyzer": "^4.4.0",
41
- "webpack-cli": "^4.5.0",
42
- "webpack-dev-server": "^3.11.0",
43
- "webpack-merge": "^5.7.3",
42
+ "webpack": "^5.63.0",
43
+ "webpack-bundle-analyzer": "^4.5.0",
44
+ "webpack-cli": "^4.9.1",
45
+ "webpack-dev-server": "^4.4.0",
46
+ "webpack-merge": "^5.8.0",
44
47
  "wildcard": "^2.0.0"
45
48
  }
46
49
  }
@@ -12,7 +12,19 @@ module.exports = (resolver, config) => {
12
12
 
13
13
  // convert to imports-loader? syntax
14
14
  if (imports && imports.length) {
15
- uses.push(`imports-loader?type=commonjs&imports=${imports.join(',')}`);
15
+ let importParams = Object.assign(
16
+ {
17
+ type: 'commonjs',
18
+ imports: imports.filter(item => typeof item === 'string').join(',')
19
+ },
20
+ ...imports.filter(item => typeof item === 'object')
21
+ );
22
+
23
+ importParams = Object.entries(importParams)
24
+ .filter(([, value]) => Boolean(value))
25
+ .map(([key, value]) => `${key}=${value.replace(/ /g, '%20')}`);
26
+
27
+ uses.push(`imports-loader?${importParams.join('&')}`);
16
28
  }
17
29
 
18
30
  // convert to exports-loader? syntax
@@ -0,0 +1,22 @@
1
+ const merge = require('deepmerge');
2
+ const StyleLoader = require('./style-loader');
3
+
4
+ class AdminStyleLoader extends StyleLoader {
5
+ /**
6
+ * @inheritdoc
7
+ */
8
+ _fetchThemeConfig(themeName) {
9
+ const {rtl_support: rtlSupport = false, styles: extraThemeConfig} =
10
+ this._configLoader.loadConfig(themeName, 'Resources/public/themes/' + themeName + '/settings.yml');
11
+ const baseThemeConfig = this._configLoader.loadConfig(themeName, 'Resources/config/oro/assets.yml');
12
+ /** @type {Object.<string, ThemeGroupConfig>} */
13
+ const themeConfig = merge(baseThemeConfig, extraThemeConfig);
14
+
15
+ return {
16
+ themeConfig,
17
+ settings: {rtlSupport}
18
+ };
19
+ }
20
+ }
21
+
22
+ module.exports = AdminStyleLoader;
@@ -1,115 +1,18 @@
1
- const path = require('path');
2
- const wildcard = require('wildcard');
3
- const _ = require('underscore')
1
+ const StyleLoader = require('./style-loader');
4
2
 
5
- class LayoutStyleLoader {
3
+ class LayoutStyleLoader extends StyleLoader {
6
4
  /**
7
- * @param {YamlConfigLoader} configLoader
8
- * @param {SCSSEntryPointFileWriter} entryPointFileWriter
5
+ * @inheritdoc
9
6
  */
10
- constructor(configLoader, entryPointFileWriter) {
11
- this._configLoader = configLoader;
12
- this._entryPointFileWriter = entryPointFileWriter;
13
- }
14
-
15
- /**
16
- * @param {string} theme Theme name
17
- * @param {string} buildPath Build path
18
- * @return {Object} List of Webpack entry-points
19
- */
20
- getThemeEntryPoints(theme, buildPath) {
21
- const entryPoints = {};
22
- const {rtl_support: rtlSupport = false} = this._configLoader.themes[theme];
23
- const themeConfig = this._configLoader.loadConfig(theme, '/config/assets.yml');
24
- const writingOptions = {};
25
-
26
- for (const [key, config] of Object.entries(themeConfig)) {
27
- let {inputs, output, auto_rtl_inputs: rtlMasks = []} = config;
28
- if (config.output === undefined) {
29
- throw new Error('"output" for "' + key + '" group in theme "' + theme + '" is not defined');
30
- }
31
- inputs = this._overrideInputs(inputs);
32
- inputs = this._sortInputs(inputs);
33
- if (rtlSupport) {
34
- writingOptions.ignoreRTLInputs = _.difference(inputs, this._matchInputs(rtlMasks, inputs));
35
- }
36
-
37
- const entryPointName = output.replace(/\.[^/.]+$/, '');
38
- const filePath = path.join(buildPath, output);
39
- entryPoints[entryPointName] =
40
- this._entryPointFileWriter.write('./../../../', inputs, filePath, writingOptions);
41
- }
42
- return entryPoints;
43
- }
44
-
45
- /**
46
- * @param {Object} inputs
47
- * @returns {string[]} List of inputs
48
- * @private
49
- */
50
- _overrideInputs(inputs) {
51
- const newInputs = [];
52
-
53
- inputs.forEach((input, index) => {
54
- if (typeof input !== 'string') {
55
- const oldInput = Object.keys(input)[0];
56
- const newInput = input[oldInput];
57
- const oldInputIndex = newInputs.findIndex(element => element === oldInput);
58
-
59
- if (newInput) { // replace input
60
- newInputs[oldInputIndex] = newInput;
61
- } else { // delete input
62
- newInputs.splice(oldInputIndex, 1);
63
- }
64
- newInputs.splice(index);
65
- } else {
66
- newInputs[index] = input;
67
- }
68
- });
69
-
70
- return newInputs;
71
- }
72
-
73
- /**
74
- * Sort inputs, so first will be settings, then variables, then other files
75
- * @param {Object} inputs
76
- * @returns {string[]} List of ordered inputs
77
- * @private
78
- */
79
- _sortInputs(inputs) {
80
- const settingsInputs = [];
81
- const variablesInputs = [];
82
- const restInputs = [];
83
-
84
- inputs.forEach(input => {
85
- if (input.indexOf('/settings/') > 0) {
86
- settingsInputs.push(input);
87
- } else if (input.indexOf('/variables/') > 0) {
88
- variablesInputs.push(input);
89
- } else {
90
- restInputs.push(input);
91
- }
92
- });
93
- return [...settingsInputs, ...variablesInputs, ...restInputs];
94
- }
95
-
96
- /**
97
- * Filter inputs that matches any mask from the list
98
- *
99
- * @param {[string]} masks list of wildcard masks
100
- * @param {[string]} inputs
101
- * @return {[string]} matched inputs
102
- * @private
103
- */
104
- _matchInputs(masks, inputs) {
105
- masks = masks.map(mask => wildcard(mask));
106
-
107
- const whiteListedInputs = masks.reduce((include, mask) => {
108
- include.push(...mask.match(inputs));
109
- return include;
110
- }, []);
111
-
112
- return _.unique(whiteListedInputs);
7
+ _fetchThemeConfig(themeName) {
8
+ const {rtl_support: rtlSupport = false} = this._configLoader.themes[themeName];
9
+ /** @type {Object.<string, ThemeGroupConfig>} */
10
+ const themeConfig = this._configLoader.loadConfig(themeName, 'config/assets.yml');
11
+
12
+ return {
13
+ themeConfig,
14
+ settings: {rtlSupport}
15
+ };
113
16
  }
114
17
  }
115
18
 
@@ -1,78 +1,162 @@
1
- const merge = require('deepmerge');
1
+ const path = require('path');
2
+ const wildcard = require('wildcard');
3
+ const _ = require('underscore');
4
+ const messages = require('../messages')
5
+
6
+ /**
7
+ * @typedef ThemeGroupConfig
8
+ * @type {Object}
9
+ * @property {Array.<string|Object>} inputs - list of input resources for import into root SCSS file
10
+ * @property {Array.<string>} entries - list of direct entry resources
11
+ * @property {string} output - name or out put resource
12
+ * @property {[boolean]} auto_rtl_inputs - whether theme support RTL
13
+ */
2
14
 
3
15
  class StyleLoader {
4
16
  /**
5
17
  * @param {YamlConfigLoader} configLoader
18
+ * @param {SCSSEntryPointFileWriter} entryPointFileWriter
6
19
  */
7
- constructor(configLoader) {
20
+ constructor(configLoader, entryPointFileWriter) {
8
21
  this._configLoader = configLoader;
22
+ this._entryPointFileWriter = entryPointFileWriter;
9
23
  }
10
24
 
11
25
  /**
12
26
  * @param {string} theme Theme name
27
+ * @param {string} buildPath Build path
13
28
  * @return {Object} List of Webpack entry-points
14
29
  */
15
- getThemeEntryPoints(theme) {
30
+ getThemeEntryPoints(theme, buildPath) {
31
+ const {themeConfig, settings = {}} = this._fetchThemeConfig(theme);
32
+
16
33
  const entryPoints = {};
17
- const commonConfig = this._configLoader.loadConfig(theme, '/Resources/config/oro/assets.yml');
18
- let themeConfig = this._configLoader.loadConfig(theme, '/Resources/public/themes/' + theme + '/settings.yml');
19
-
20
- themeConfig = themeConfig.styles;
21
- for (const key in themeConfig) {
22
- if (themeConfig.hasOwnProperty(key)) {
23
- const config = themeConfig[key];
24
- if (commonConfig[key]) {
25
- commonConfig[key] = merge(commonConfig[key], config);
26
- }
34
+ const writingOptions = {};
35
+ for (const [group, config] of Object.entries(themeConfig)) {
36
+ let {inputs, entries = [], output, auto_rtl_inputs: rtlMasks = []} = config;
37
+
38
+ if (output === void 0) {
39
+ throw new Error(messages.assetsMissedOutput(group, theme));
27
40
  }
28
- }
29
41
 
30
- for (const key in commonConfig) {
31
- if (commonConfig.hasOwnProperty(key)) {
32
- const config = commonConfig[key];
33
- const inputs = this._overrideInputs(config.inputs);
34
- if (config.output === undefined) {
35
- throw new Error('"output" for "' + key + '" group in theme "' + theme + '" is not defined');
36
- }
37
- entryPoints[config.output.replace(/\.[^/.]+$/, '')] = inputs;
42
+ if (inputs === void 0) {
43
+ throw new Error(messages.assetsMissedInput(group, theme));
38
44
  }
45
+
46
+ inputs = this._overrideInputs(inputs);
47
+ inputs = this._sortInputs(inputs);
48
+
49
+ if (settings.rtlSupport) {
50
+ writingOptions.ignoreRTLInputs = _.difference(inputs, this._matchInputs(rtlMasks, inputs));
51
+ }
52
+
53
+ const entryPointName = output.replace(/\.[^/.]+$/, '');
54
+ const filePath = path.join(buildPath, output);
55
+ entryPoints[entryPointName] = [
56
+ ...entries,
57
+ this._entryPointFileWriter.write('./../../../', inputs, filePath, writingOptions)
58
+ ];
39
59
  }
40
60
  return entryPoints;
41
61
  }
42
62
 
43
63
  /**
44
- * @param {Object} inputs
64
+ * Extracts theme configuration with its settings
65
+ *
66
+ * @param {string} themeName
67
+ * @return {{themeConfig: Object.<string, ThemeGroupConfig>, settings: {rtlSupport: boolean}}}
68
+ * @abstract
69
+ * @protected
70
+ */
71
+ _fetchThemeConfig(themeName) {
72
+ throw new Error('Method `_fetchThemeConfig` has to be implemented in extends');
73
+ }
74
+
75
+ /**
76
+ * Override inputs
77
+ *
78
+ * @param {(string|Object)[]} inputs
45
79
  * @returns {string[]} List of inputs
46
- * @private
80
+ * @protected
47
81
  */
48
82
  _overrideInputs(inputs) {
49
- const newInputs = [];
50
-
51
- inputs.forEach((input, index) => {
52
- if (typeof input !== 'string') {
53
- const oldInput = Object.keys(input)[0];
54
- const newInput = input[oldInput];
55
- const oldInputIndex = newInputs.findIndex(element => element === oldInput);
56
-
57
- if (newInput) { // replace input
58
- newInputs[oldInputIndex] = newInput;
59
- } else { // delete input
60
- newInputs.splice(oldInputIndex, 1);
61
- }
62
- newInputs.splice(index);
83
+ const mappedInputs = [];
84
+ inputs.forEach(input => {
85
+ if (typeof input === 'string') {
86
+ mappedInputs.push(input);
63
87
  } else {
64
- // cut off ~ prefix from the path,
65
- // if someone acidentaly used it for the admin theme assets.yml or settings.yml file,
66
- // because the syntax is valid only in the @import statement, that is
67
- // not used for the admin theme yaml files configuration
68
- if (input.startsWith('~')) {
69
- input = input.substr(1);
88
+ const [oldInput, newInput] = Object.entries(input)[0];
89
+ const oldInputIndex = mappedInputs.findIndex(item => item === oldInput);
90
+
91
+ if (oldInputIndex === -1) {
92
+ // old input does not exists any more
93
+ mappedInputs.push(newInput);
94
+ } else if (newInput) {
95
+ // replace input
96
+ mappedInputs[oldInputIndex] = newInput;
97
+ } else {
98
+ // delete input
99
+ mappedInputs.splice(oldInputIndex, 1);
70
100
  }
71
- newInputs.push(input);
72
101
  }
73
102
  });
103
+ return mappedInputs;
104
+ }
105
+
106
+ /**
107
+ * Sort inputs, so first will be settings, then variables, then other files
108
+ *
109
+ * @param {string[]} inputs
110
+ * @returns {string[]} List of ordered inputs
111
+ * @protected
112
+ */
113
+ _sortInputs(inputs) {
114
+ const primarySettingsInputs = [];
115
+ const settingsInputs = [];
116
+ const primaryVariablesInputs = [];
117
+ const variablesInputs = [];
118
+ const restInputs = [];
119
+
120
+ inputs.forEach(input => {
121
+ if (input.indexOf('/settings/primary-settings') > 0) {
122
+ primarySettingsInputs.push(input);
123
+ } else if (input.indexOf('/settings/') > 0) {
124
+ settingsInputs.push(input);
125
+ } else if (input.indexOf('/variables/primary-variables') > 0) {
126
+ primaryVariablesInputs.push(input);
127
+ } else if (input.indexOf('/variables/') > 0) {
128
+ variablesInputs.push(input);
129
+ } else {
130
+ restInputs.push(input);
131
+ }
132
+ });
133
+
134
+ return [
135
+ ...primarySettingsInputs,
136
+ ...settingsInputs,
137
+ ...primaryVariablesInputs,
138
+ ...variablesInputs,
139
+ ...restInputs
140
+ ];
141
+ }
142
+
143
+ /**
144
+ * Filter inputs that matches any mask from the list
145
+ *
146
+ * @param {string[]} masks list of wildcard masks
147
+ * @param {string[]} inputs
148
+ * @return {string[]} matched inputs
149
+ * @protected
150
+ */
151
+ _matchInputs(masks, inputs) {
152
+ masks = masks.map(mask => wildcard(mask));
153
+
154
+ const whiteListedInputs = masks.reduce((include, mask) => {
155
+ include.push(...mask.match(inputs));
156
+ return include;
157
+ }, []);
74
158
 
75
- return newInputs;
159
+ return _.unique(whiteListedInputs);
76
160
  }
77
161
  }
78
162
 
@@ -1,3 +1,5 @@
1
+ const messages = require('./messages');
2
+
1
3
  class ThemeConfigFactory {
2
4
  /**
3
5
  * @param {ModulesConfigLoader} configLoader
@@ -11,15 +13,56 @@ class ThemeConfigFactory {
11
13
  this._appModulesFileWriter = appModulesFileWriter;
12
14
  this._configsFileWriter = configsFileWriter;
13
15
  }
14
-
15
16
  /**
16
17
  * @param {string} theme Theme name
18
+ * @param {string|string[]} configFilepath Path (or paths with fallback) to yaml config file in a bundle
19
+ * @return {Object} Merged Configs loaded from all the bundles Yaml files matched by filePath
20
+ */
21
+ loadConfig(theme, configFilepath) {
22
+ try {
23
+ return this._configLoader.loadConfig(theme, configFilepath)
24
+ } catch (e) {
25
+ throw new Error(messages.jsModulesError(theme));
26
+ }
27
+ }
28
+
29
+ extendConfig(baseConfig, extraConfig) {
30
+ const {
31
+ aliases = {},
32
+ configs,
33
+ map = {},
34
+ shim = {},
35
+ } = baseConfig;
36
+
37
+ const {
38
+ ['app-modules']: appModules,
39
+ ['dynamic-imports']: dynamicImports,
40
+ entry,
41
+ ...rest
42
+ } = extraConfig;
43
+
44
+ const beyondKeys = Object.keys(rest);
45
+ if (beyondKeys.length) {
46
+ throw new Error( messages.jsExtraModulesError(beyondKeys));
47
+ }
48
+
49
+ return {
50
+ aliases,
51
+ 'app-modules': appModules,
52
+ configs,
53
+ 'dynamic-imports': dynamicImports,
54
+ entry,
55
+ map,
56
+ shim
57
+ };
58
+ }
59
+
60
+ /**
17
61
  * @param {string} buildPath Path to theme build folder
18
- * @param {string} configFilepath Path to yaml config file in a bundle
19
- * @return {Object} List of Webpack entry-points
62
+ * @param {Object} jsModulesConfig configuration loaded from jsmodules files and merged together
63
+ * @return {Object} webpack configuration fragment
20
64
  */
21
- create(theme, buildPath, configFilepath) {
22
- const jsModulesConfig = this._configLoader.loadConfig(theme, configFilepath);
65
+ create(buildPath, jsModulesConfig) {
23
66
  const {
24
67
  entry,
25
68
  map = {},
@@ -12,7 +12,7 @@ class ConfigsFileWriter {
12
12
  /**
13
13
  * Write app-modules.js file and return file path
14
14
  *
15
- * @param {Array} configs List of configurable modules
15
+ * @param {Object} configs List of configurable modules
16
16
  * @param {string} output Output file path
17
17
  * @returns {string} JS file path of an output file
18
18
  */
@@ -19,9 +19,9 @@ class DynamicImportsFileWriter {
19
19
  write(dynamicImports, output) {
20
20
  const buildPath = path.join(output, 'dynamic-imports.js');
21
21
  let content = Object.entries(dynamicImports).map(([chunkName, moduleNames]) => {
22
- return moduleNames.map(moduleName =>
23
- `'${moduleName}': function() { return import(/* webpackChunkName: "${chunkName}" */ '${moduleName}') }`
24
- );
22
+ const notation = chunkName === 'commons' ?
23
+ '/* webpackMode: "eager" */' : `/* webpackChunkName: "${chunkName}" */`;
24
+ return moduleNames.map(moduleName =>`'${moduleName}': () => import(${notation}'${moduleName}')`);
25
25
  });
26
26
  content = `module.exports = {\n ${content.flat().join(',\n ')}\n};\n`;
27
27
  const filepath = path.resolve(this._publicPath + buildPath);