@oroinc/oro-webpack-config-builder 5.1.0-alpha32 → 5.1.0-alpha35

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.
@@ -0,0 +1,95 @@
1
+ const BaseError = require('./validation/errors/base-error');
2
+ const {isVerboseMode} = require('./utils');
3
+ const {red, yellow, green, bgRed} = require('colorette');
4
+
5
+ const emptyLine = length => new Array(length).fill(' ').join('');
6
+ const multiline = (color, msg) => {
7
+ msg = ` ${msg}`;
8
+ msg = msg + emptyLine(120 - msg.length);
9
+
10
+ return `\n${color(emptyLine(msg.length))}\n${color(msg)}\n${color(emptyLine(msg.length))}`;
11
+ };
12
+
13
+ class ErrorHandler {
14
+ constructor() {
15
+ this.failedThemes = [];
16
+
17
+ process.on('beforeExit', code => {
18
+ this.onProcess();
19
+ });
20
+ }
21
+
22
+ /**
23
+ * Show a message about failed themes
24
+ */
25
+ onProcess() {
26
+ if (this.failedThemes.length) {
27
+ let msg = '[ERROR] Assets build';
28
+
29
+ if (this.failedThemes.length === 1) {
30
+ msg = `${msg} for "${this.failedThemes[0]}" theme failed.`;
31
+ } else {
32
+ msg = `${msg} for "${this.failedThemes.join(', ')}" themes failed.`;
33
+ }
34
+ console.error(multiline(bgRed, msg));
35
+ process.exit(1);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Executes when the Webpack Compiler will complete a compilation
41
+ * In case of empty Webpack Config this method will not run
42
+ *
43
+ * @param {Object} stats
44
+ */
45
+ onBuildComplete(stats) {
46
+ const failed = stats?.compilation?.errors.length !== 0;
47
+
48
+ if (failed) {
49
+ console.error(multiline(bgRed, '[ERROR] Assets build failed.'));
50
+ process.exit(1);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * @param {Array|string} errors
56
+ * @param {string} [failedTheme]
57
+ */
58
+ displayError(errors, failedTheme) {
59
+ if (typeof failedTheme === 'string') {
60
+ this.failedThemes.push(failedTheme);
61
+ }
62
+
63
+ if (typeof errors === 'string') {
64
+ errors = [errors];
65
+ }
66
+
67
+ const errorMessage = errors.map(err => {
68
+ const highlightPrefix = str => {
69
+ const prefix = str.substring(0, str.search(':'));
70
+ return red(prefix) + str.substring(str.search(':'));
71
+ };
72
+
73
+ if (err instanceof BaseError) {
74
+ err.message = `${err.message}\n${green(err.extra)}`;
75
+ }
76
+
77
+ let msg = err.message;
78
+
79
+ if (isVerboseMode()) {
80
+ msg = highlightPrefix(err.stack);
81
+ } else {
82
+ const command = yellow('php bin/console oro:assets:build --env=dev --verbose');
83
+ const info = `${green(`Run the command ${command} to see more information`)}`;
84
+
85
+ msg = highlightPrefix(`${err.name}: ${msg}\n${info}`);
86
+ }
87
+
88
+ return `${msg}\n`;
89
+ });
90
+
91
+ console.error(errorMessage.join('\n'));
92
+ }
93
+ }
94
+
95
+ module.exports = ErrorHandler;
@@ -1,70 +1 @@
1
- module.exports = function (e) {
2
- var n = {};
3
-
4
- function t(i) {
5
- if (n[i]) return n[i].exports;
6
- var r = n[i] = {i: i, l: !1, exports: {}};
7
- return e[i].call(r.exports, r, r.exports, t), r.l = !0, r.exports
8
- }
9
-
10
- return t.m = e, t.c = n, t.d = function (e, n, i) {
11
- t.o(e, n) || Object.defineProperty(e, n, {configurable: !1, enumerable: !0, get: i})
12
- }, t.r = function (e) {
13
- Object.defineProperty(e, "__esModule", {value: !0})
14
- }, t.n = function (e) {
15
- var n = e && e.__esModule ? function () {
16
- return e.default
17
- } : function () {
18
- return e
19
- };
20
- return t.d(n, "a", n), n
21
- }, t.o = function (e, n) {
22
- return Object.prototype.hasOwnProperty.call(e, n)
23
- }, t.p = "", t.w = {}, t(t.s = 3)
24
- }([function (e, n) {
25
- e.exports = require("babel-core")
26
- }, function (e, n, t) {
27
- "use strict";
28
- Object.defineProperty(n, "__esModule", {value: !0});
29
- var i = t(0);
30
- n.default = (0, i.template)("\n module.exports = function __injector(__injections) {\n __injections = __injections || {};\n\n (function __validateInjection() {\n var validDependencies = DEPENDENCIES;\n var injectedDependencies = Object.keys(__injections);\n var invalidInjectedDependencies = injectedDependencies.filter(function (dependency) {\n return validDependencies.indexOf(dependency) === -1;\n });\n\n if (invalidInjectedDependencies.length > 0) {\n var validDependenciesString = ' - ' + validDependencies.join('\\n - ');\n var injectedDependenciesString = ' - ' + injectedDependencies.join('\\n - ');\n var invalidDependenciesString = ' - ' + invalidInjectedDependencies.join('\\n - ');\n\n throw new Error('Injection Error in ' + SOURCE_PATH + '\\n\\n' +\n 'The following injections are invalid:\\n' + invalidDependenciesString + '\\n\\n' +\n 'The following injections were passed in:\\n' + injectedDependenciesString + '\\n\\n' +\n 'Valid injection targets for this module are:\\n' + validDependenciesString + '\\n'\n );\n }\n })();\n\n __injector.sourcePath = SOURCE_PATH;\n __injector.validDependencies = DEPENDENCIES;\n\n var module = { exports: {} };\n var exports = module.exports;\n\n (function () {\n SOURCE\n })();\n\n return module.exports;\n }\n"), e.exports = n.default
31
- }, function (e, n, t) {
32
- "use strict";
33
- Object.defineProperty(n, "__esModule", {value: !0}), n.default = function (e, n, t) {
34
- var i = (0, r.transform)(n, {babelrc: !1, code: !1, compact: !1, filename: e.resourcePath}).ast, o = [];
35
- (0, r.traverse)(i, {
36
- CallExpression: function (e) {
37
- r.types.isIdentifier(e.node.callee, {name: "require"}) && (o.push(function (e) {
38
- var n = e.node.arguments[0].value;
39
- return e.replaceWith(r.types.expressionStatement(r.types.conditionalExpression(r.types.callExpression(r.types.memberExpression(r.types.identifier("__injections"), r.types.identifier("hasOwnProperty"), !1), [r.types.stringLiteral(n)]), r.types.memberExpression(r.types.identifier("__injections"), r.types.stringLiteral(n), !0), e.node))), n
40
- }(e)), e.skip())
41
- }
42
- }), 0 === o.length && e.emitWarning("The module you are trying to inject into doesn't have any dependencies. Are you sure you want to do this?");
43
- var a = r.types.file(r.types.program([(0, s.default)({
44
- SOURCE: i,
45
- SOURCE_PATH: r.types.stringLiteral(e.resourcePath),
46
- DEPENDENCIES: r.types.arrayExpression(o.map(function (e) {
47
- return r.types.stringLiteral(e)
48
- }))
49
- })]));
50
- return (0, r.transformFromAst)(a, n, {
51
- sourceMaps: e.sourceMap,
52
- sourceFileName: e.resourcePath,
53
- inputSourceMap: t,
54
- babelrc: !1,
55
- compact: !1,
56
- filename: e.resourcePath
57
- })
58
- };
59
- var i, r = t(0), o = t(1), s = (i = o) && i.__esModule ? i : {default: i};
60
- e.exports = n.default
61
- }, function (e, n, t) {
62
- "use strict";
63
- Object.defineProperty(n, "__esModule", {value: !0}), n.default = function (e, n) {
64
- this.cacheable && this.cacheable();
65
- var t = (0, o.default)(this, e, n), i = t.code, r = t.map;
66
- this.callback(null, i, r)
67
- };
68
- var i, r = t(2), o = (i = r) && i.__esModule ? i : {default: i};
69
- e.exports = n.default
70
- }]);
1
+ module.exports=function(e){var n={};function t(i){if(n[i])return n[i].exports;var r=n[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,t),r.l=!0,r.exports}return t.m=e,t.c=n,t.d=function(e,n,i){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:i})},t.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t.w={},t(t.s=3)}([function(e,n){e.exports=require("babel-core")},function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var i=t(0);n.default=(0,i.template)("\n module.exports = function __injector(__injections) {\n __injections = __injections || {};\n\n (function __validateInjection() {\n var validDependencies = DEPENDENCIES;\n var injectedDependencies = Object.keys(__injections);\n var invalidInjectedDependencies = injectedDependencies.filter(function (dependency) {\n return validDependencies.indexOf(dependency) === -1;\n });\n\n if (invalidInjectedDependencies.length > 0) {\n var validDependenciesString = ' - ' + validDependencies.join('\\n - ');\n var injectedDependenciesString = ' - ' + injectedDependencies.join('\\n - ');\n var invalidDependenciesString = ' - ' + invalidInjectedDependencies.join('\\n - ');\n\n throw new Error('Injection Error in ' + SOURCE_PATH + '\\n\\n' +\n 'The following injections are invalid:\\n' + invalidDependenciesString + '\\n\\n' +\n 'The following injections were passed in:\\n' + injectedDependenciesString + '\\n\\n' +\n 'Valid injection targets for this module are:\\n' + validDependenciesString + '\\n'\n );\n }\n })();\n\n __injector.sourcePath = SOURCE_PATH;\n __injector.validDependencies = DEPENDENCIES;\n\n var module = { exports: {} };\n var exports = module.exports;\n\n (function () {\n SOURCE\n })();\n\n return module.exports;\n }\n"),e.exports=n.default},function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=function(e,n,t){var i=(0,r.transform)(n,{babelrc:!1,code:!1,compact:!1,filename:e.resourcePath}).ast,o=[];(0,r.traverse)(i,{CallExpression:function(e){r.types.isIdentifier(e.node.callee,{name:"require"})&&(o.push(function(e){var n=e.node.arguments[0].value;return e.replaceWith(r.types.expressionStatement(r.types.conditionalExpression(r.types.callExpression(r.types.memberExpression(r.types.identifier("__injections"),r.types.identifier("hasOwnProperty"),!1),[r.types.stringLiteral(n)]),r.types.memberExpression(r.types.identifier("__injections"),r.types.stringLiteral(n),!0),e.node))),n}(e)),e.skip())}}),0===o.length&&e.emitWarning("The module you are trying to inject into doesn't have any dependencies. Are you sure you want to do this?");var a=r.types.file(r.types.program([(0,s.default)({SOURCE:i,SOURCE_PATH:r.types.stringLiteral(e.resourcePath),DEPENDENCIES:r.types.arrayExpression(o.map(function(e){return r.types.stringLiteral(e)}))})]));return(0,r.transformFromAst)(a,n,{sourceMaps:e.sourceMap,sourceFileName:e.resourcePath,inputSourceMap:t,babelrc:!1,compact:!1,filename:e.resourcePath})};var i,r=t(0),o=t(1),s=(i=o)&&i.__esModule?i:{default:i};e.exports=n.default},function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=function(e,n){this.cacheable&&this.cacheable();var t=(0,o.default)(this,e,n),i=t.code,r=t.map;this.callback(null,i,r)};var i,r=t(2),o=(i=r)&&i.__esModule?i:{default:i};e.exports=n.default}]);
@@ -8,13 +8,17 @@ class LayoutModulesConfigLoader extends ModulesConfigLoader {
8
8
  */
9
9
  loadConfig(theme, filePath) {
10
10
  let themeConfig = super.loadConfig(theme, path.join('Resources/views/layouts/', theme, filePath));
11
+ themeConfig = merge(themeConfig, super.loadConfig(theme, path.join('templates/layouts/', theme, filePath)));
11
12
  // recursive process parent theme
12
13
  const {parent: parentTheme} = this.themes[theme];
13
14
  if (typeof parentTheme === 'string') {
15
+ const processedFiles = this.processedFiles;
14
16
  const parentThemeConfig = this.loadConfig(parentTheme, filePath);
17
+
15
18
  themeConfig = merge(parentThemeConfig, themeConfig);
19
+ // processedFiles from parent theme is added to processedFiles of current theme
20
+ this._processedFiles = [...processedFiles, ...this.processedFiles];
16
21
  }
17
-
18
22
  return themeConfig;
19
23
  }
20
24
 
@@ -40,7 +44,7 @@ class LayoutModulesConfigLoader extends ModulesConfigLoader {
40
44
  * @return {string[]}
41
45
  */
42
46
  extraJSBuildNames(theme) {
43
- const {extra_js_builds: extraJSBuilds = []} = this._themes[theme] || {}
47
+ const {extra_js_builds: extraJSBuilds = []} = this._themes[theme] || {};
44
48
  return [...extraJSBuilds.map(suffix => `${theme}-${suffix}`)];
45
49
  }
46
50
 
@@ -66,7 +70,7 @@ class LayoutModulesConfigLoader extends ModulesConfigLoader {
66
70
  const result = [];
67
71
  if (marches) {
68
72
  const [, theme, suffix] = marches;
69
- const {extra_js_builds: extraJSBuilds = []} = this._themes[theme] || {}
73
+ const {extra_js_builds: extraJSBuilds = []} = this._themes[theme] || {};
70
74
  if (extraJSBuilds.includes(suffix)) {
71
75
  result.push(marches[1], marches[2]);
72
76
  } else {
@@ -2,6 +2,7 @@ const path = require('path');
2
2
  const fs = require('fs');
3
3
  const merge = require('deepmerge');
4
4
  const yaml = require('js-yaml');
5
+ const validation = require('../validation');
5
6
 
6
7
  // merge only unique items
7
8
  const arrayMerge = (target, source) => target.concat(source.filter(item => !target.includes(item)));
@@ -28,25 +29,43 @@ class ModulesConfigLoader {
28
29
  return this.themeNames;
29
30
  }
30
31
 
32
+ /**
33
+ * @returns {Array}
34
+ */
35
+ get processedFiles() {
36
+ return [...this._processedFiles];
37
+ }
38
+
31
39
  /**
32
40
  * @param {Array} bundles Array of ordered symfony bundle paths
33
- * @param {string} themesLocation Path inside the bundle, where to find the theme
41
+ * @param {string|Array} themesLocation Path inside the bundle, where to find the theme
34
42
  * @param {string} themeInfoFileName Yaml File name with theme info
35
43
  */
36
44
  constructor(bundles, themesLocation, themeInfoFileName) {
37
45
  this._bundles = bundles;
38
- this._themes = this._getThemes(themesLocation, themeInfoFileName);
46
+ if (!Array.isArray(themesLocation)) {
47
+ themesLocation = [themesLocation];
48
+ }
49
+
50
+ const themes = {};
51
+ const self = this;
52
+ themesLocation.forEach(themesLocation => {
53
+ self._collectThemes(themes, themesLocation, themeInfoFileName);
54
+ });
55
+
56
+ this._themes = themes;
57
+ this._processedFiles = [];
39
58
  }
40
59
 
41
60
  /**
42
- * Return list of themes with their parents
61
+ * Collects list of themes with their parents into given storage(themes param)
62
+ *
63
+ * @param {Object} themes
43
64
  * @param {string} themesLocation
44
65
  * @param {string} themeInfoFileName
45
- * @returns {Object.<string|null>}
46
66
  * @private
47
67
  */
48
- _getThemes(themesLocation, themeInfoFileName) {
49
- const themes = {};
68
+ _collectThemes(themes, themesLocation, themeInfoFileName) {
50
69
  this._bundles.forEach(bundle => {
51
70
  const source = bundle + themesLocation;
52
71
 
@@ -64,8 +83,6 @@ class ModulesConfigLoader {
64
83
  themes[name] = merge(themes[name] || {}, theme, {arrayMerge});
65
84
  });
66
85
  });
67
-
68
- return themes;
69
86
  }
70
87
 
71
88
  /**
@@ -76,6 +93,8 @@ class ModulesConfigLoader {
76
93
  loadConfig(theme, filePath) {
77
94
  let configs = {};
78
95
  const filePaths = [].concat(filePath);
96
+
97
+ this._processedFiles = [];
79
98
  this._bundles.forEach(bundle => {
80
99
  let absolutePath;
81
100
 
@@ -88,10 +107,16 @@ class ModulesConfigLoader {
88
107
  }
89
108
 
90
109
  if (absolutePath) {
91
- const doc = yaml.load(fs.readFileSync(absolutePath, 'utf8'));
110
+ const rawDoc = fs.readFileSync(absolutePath, 'utf8');
111
+ const doc = yaml.load(rawDoc);
112
+
113
+ this._processedFiles.push(absolutePath);
114
+
115
+ validation.checkSchema(absolutePath, doc, theme);
92
116
  configs = merge(configs, doc);
93
117
  }
94
118
  });
119
+
95
120
  return configs;
96
121
  }
97
122
  }
@@ -3,6 +3,7 @@ const printf = require('printf');
3
3
  const AppConfigLoader = require('./app-config-loader');
4
4
  const AppModulesFileWriter = require('./writer/app-modules-file-writer');
5
5
  const CleanupStatsPlugin = require('./plugin/stats/cleanup-stats-plugin');
6
+ const AfterWebpackLogsPlugin = require('./plugin/logs/after-webpack-logs-plugin');
6
7
  const ConfigsFileWriter = require('./writer/configs-file-writer');
7
8
  const EntryPointFileWriter = require('./writer/scss-entry-point-file-writer');
8
9
  const LayoutModulesConfigLoader = require('./modules-config/layout-modules-config-loader');
@@ -23,33 +24,45 @@ const resolve = require('enhanced-resolve');
23
24
  const {merge: webpackMerge} = require('webpack-merge');
24
25
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
25
26
  const RtlCssWebpackPlugin = require('rtlcss-webpack-plugin');
26
- const {red: colorRed} = require('colorette');
27
+ const validation = require('./validation');
28
+ const EventEmitter = require('events');
29
+ const ErrorHandler = require('./error-handler');
30
+
27
31
  require('resolve-url-loader');
28
32
 
29
33
  class ConfigBuilder {
30
34
  constructor() {
31
- this._publicPath = 'public/';
32
- this._adminTheme = 'admin.oro';
35
+ this._projectPath = '';
33
36
  this._enableLayoutThemes = false;
34
37
  this._defaultLayoutThemes = null;
35
- this._versionFormat = '%s?v=%s';
36
- this._babelConfig = {
38
+ this.emitter = new EventEmitter();
39
+ this.errorHandler = new ErrorHandler();
40
+ this.addListeners();
41
+ this.setPublicPath('public/');
42
+ this.setAdminTheme('admin.oro');
43
+ this.setVersionFormat('%s?v=%s');
44
+ this.setBabelConfig({
37
45
  sourceType: 'unambiguous',
38
46
  presets: [
39
47
  [
40
48
  '@babel/preset-env', {
41
- useBuiltIns: 'usage',
42
- corejs: {
43
- version: 3,
44
- proposals: true
49
+ useBuiltIns: 'usage',
50
+ corejs: {
51
+ version: 3,
52
+ proposals: true
53
+ }
45
54
  }
46
- }
47
55
  ]
48
56
  ],
49
57
  plugins: [
50
58
  '@babel/plugin-transform-runtime'
51
59
  ]
52
- };
60
+ });
61
+ }
62
+
63
+ addListeners() {
64
+ this.emitter.on('publicPath:updated', path => validation.setPublicPath(path));
65
+ this.emitter.on('build:complete', stats => this.errorHandler.onBuildComplete(stats));
53
66
  }
54
67
 
55
68
  /**
@@ -59,6 +72,7 @@ class ConfigBuilder {
59
72
  */
60
73
  setPublicPath(publicPath) {
61
74
  this._publicPath = publicPath;
75
+ this.emitter.emit('publicPath:updated', publicPath);
62
76
  return this;
63
77
  }
64
78
 
@@ -80,6 +94,7 @@ class ConfigBuilder {
80
94
  */
81
95
  setVersionFormat(versionFormat) {
82
96
  this._versionFormat = versionFormat;
97
+ this.emitter.emit('versionFormat:updated', versionFormat);
83
98
  return this;
84
99
  }
85
100
 
@@ -96,6 +111,7 @@ class ConfigBuilder {
96
111
  }
97
112
 
98
113
  this._adminTheme = adminTheme;
114
+ this.emitter.emit('adminTheme:updated', adminTheme);
99
115
  return this;
100
116
  }
101
117
 
@@ -105,8 +121,10 @@ class ConfigBuilder {
105
121
  enableLayoutThemes(themes) {
106
122
  if (themes) {
107
123
  this._defaultLayoutThemes = themes;
124
+ this.emitter.emit('defaultLayoutThemes:updated', themes);
108
125
  }
109
126
  this._enableLayoutThemes = true;
127
+ this.emitter.emit('enableLayoutThemes:updated', true);
110
128
  return this;
111
129
  }
112
130
 
@@ -117,6 +135,7 @@ class ConfigBuilder {
117
135
  */
118
136
  setBabelConfig(babelConfig) {
119
137
  this._babelConfig = babelConfig;
138
+ this.emitter.emit('babelConfig:updated', babelConfig);
120
139
  return this;
121
140
  }
122
141
 
@@ -132,19 +151,30 @@ class ConfigBuilder {
132
151
  try {
133
152
  commonConfig = this._getCommonWebpackConfig(args, env);
134
153
  } catch (e) {
135
- console.error(colorRed(`Error: ${e.message}`));
154
+ this.errorHandler.displayError(e);
136
155
  process.exit(1);
137
156
  }
138
157
 
139
158
  const webpackConfigs = [];
140
159
  const requestedBuildNames = env.theme ? env.theme.split(',') : [];
141
160
  const buildNames = this._getBuildNames(requestedBuildNames);
161
+
162
+ const buildErrors = [];
163
+ validation.jsmodulesValidator.emitter.on('error', error => buildErrors.push(error));
164
+ validation.assetsValidation.emitter.on('error', error => buildErrors.push(error));
142
165
  buildNames.forEach(buildName => {
143
166
  let buildConfig;
167
+ // flush all collected errors from previews builds
168
+ buildErrors.splice(0, buildErrors.length);
169
+
144
170
  try {
145
171
  buildConfig = this._getThemeWebpackConfig(buildName, args, env);
146
- } catch (e) {
147
- console.error(colorRed(`Error: ${e.message}`));
172
+ } catch (error) {
173
+ buildErrors.push(error);
174
+ }
175
+
176
+ if (buildErrors.length) {
177
+ this.errorHandler.displayError(buildErrors, buildName);
148
178
  return;
149
179
  }
150
180
 
@@ -157,6 +187,13 @@ class ConfigBuilder {
157
187
  };
158
188
  }
159
189
 
190
+ get resolvedProjectPath() {
191
+ if (this._resolvedProjectPath == undefined) {
192
+ this._resolvedProjectPath = path.resolve(this._projectPath);
193
+ }
194
+ return this._resolvedProjectPath;
195
+ }
196
+
160
197
  get resolvedPublicPath() {
161
198
  if (this._resolvedPublicPath === undefined) {
162
199
  this._resolvedPublicPath = path.resolve(this._publicPath);
@@ -331,7 +368,10 @@ class ConfigBuilder {
331
368
  }),
332
369
  new webpack.optimize.MinChunkSizePlugin({
333
370
  minChunkSize: 30000 // Minimum number of characters
334
- })
371
+ }),
372
+ new AfterWebpackLogsPlugin(
373
+ stats => this.emitter.emit('build:complete', stats)
374
+ )
335
375
  ]
336
376
  };
337
377
 
@@ -401,8 +441,18 @@ class ConfigBuilder {
401
441
  if (this._isAdminTheme(buildName)) {
402
442
  themeDefinition = this._modulesConfigLoader.themes[buildName.split('.')[1]];
403
443
  buildPublicPath = '/build/admin/';
404
- const jsModulesConfig = this._themeConfigFactory.loadConfig(buildName,
405
- ['Resources/config/oro/jsmodules.yml', 'Resources/config/jsmodules.yml']);
444
+ const jsModulesConfig = this._themeConfigFactory.loadConfig(
445
+ buildName,
446
+ [
447
+ 'Resources/config/oro/jsmodules.yml',
448
+ 'Resources/config/jsmodules.yml',
449
+ 'config/oro/jsmodules.yml'
450
+ ]
451
+ ); validation.jsmodulesValidator.checkFullSchema(
452
+ jsModulesConfig,
453
+ this._themeConfigFactory?._configLoader.processedFiles,
454
+ buildName
455
+ );
406
456
  jsBuildConfig = this._themeConfigFactory.create(buildPublicPath, jsModulesConfig);
407
457
  } else if (this._layoutModulesConfigLoader.isExtraJSBuild(buildName)) {
408
458
  const [theme, suffix] = this._layoutModulesConfigLoader.splitBuildName(buildName);
@@ -412,11 +462,21 @@ class ConfigBuilder {
412
462
  const baseConfig = this._layoutThemeConfigFactory.loadConfig(theme, 'config/jsmodules.yml');
413
463
  const extraConfig = this._layoutThemeConfigFactory.loadConfig(theme, `config/jsmodules-${suffix}.yml`);
414
464
  const jsModulesConfig = this._layoutThemeConfigFactory.extendConfig(baseConfig, extraConfig);
465
+ validation.jsmodulesValidator.checkFullSchema(
466
+ jsModulesConfig,
467
+ this._layoutThemeConfigFactory?._configLoader.processedFiles,
468
+ buildName
469
+ );
415
470
  jsBuildConfig = this._layoutThemeConfigFactory.create(buildPublicPath, jsModulesConfig);
416
471
  } else {
417
472
  themeDefinition = this._layoutModulesConfigLoader.themes[buildName];
418
473
  buildPublicPath = `/build/${buildName}/`;
419
474
  const jsModulesConfig = this._layoutThemeConfigFactory.loadConfig(buildName, 'config/jsmodules.yml');
475
+ validation.jsmodulesValidator.checkFullSchema(
476
+ jsModulesConfig,
477
+ this._layoutThemeConfigFactory?._configLoader.processedFiles,
478
+ buildName
479
+ );
420
480
  jsBuildConfig = this._layoutThemeConfigFactory.create(buildPublicPath, jsModulesConfig);
421
481
  }
422
482
  const {rtl_support: rtlSupport = false} = themeDefinition;
@@ -426,6 +486,7 @@ class ConfigBuilder {
426
486
  modules: [
427
487
  resolvedBuildPath,
428
488
  this.resolvedPublicPath,
489
+ path.join(this.resolvedProjectPath, '/assets'),
429
490
  path.join(this.resolvedPublicPath, '/bundles'),
430
491
  path.join(this.resolvedPublicPath, '/js'),
431
492
  this.resolvedNodeModulesPath
@@ -481,7 +542,7 @@ class ConfigBuilder {
481
542
  ...prepareModulesShim(resolver, jsBuildConfig.shim)
482
543
  ]
483
544
  }
484
- }
545
+ };
485
546
  }
486
547
 
487
548
  _initialize(args, env) {
@@ -490,10 +551,11 @@ class ConfigBuilder {
490
551
  this._isProduction = args.mode === 'production';
491
552
  this._symfonyEnv = env.symfony;
492
553
  this._appConfig = AppConfigLoader.getConfig(this._cachePath, this._symfonyEnv);
554
+ this._appConfig.paths.push(this.resolvedProjectPath);
493
555
 
494
556
  this._modulesConfigLoader = new ModulesConfigLoader(
495
557
  this._appConfig.paths,
496
- '/Resources/public/themes/',
558
+ ['/Resources/public/themes/', '/public/themes/admin/'],
497
559
  'settings.yml'
498
560
  );
499
561
  this._adminThemes = this._modulesConfigLoader.themeNames.map(themeName => 'admin.' + themeName);
@@ -507,7 +569,7 @@ class ConfigBuilder {
507
569
 
508
570
  this._layoutModulesConfigLoader = new LayoutModulesConfigLoader(
509
571
  this._appConfig.paths,
510
- '/Resources/views/layouts/',
572
+ ['/Resources/views/layouts/', '/templates/layouts/'],
511
573
  'theme.yml'
512
574
  );
513
575
  this._layoutStyleLoader = new LayoutStyleLoader(this._layoutModulesConfigLoader, entryPointFileWriter);
@@ -520,7 +582,7 @@ class ConfigBuilder {
520
582
  }
521
583
 
522
584
  _getVersionedPath(name, assetVersion) {
523
- if(!assetVersion) {
585
+ if (!assetVersion) {
524
586
  return name;
525
587
  }
526
588
  return printf(this._versionFormat, name, assetVersion);
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@oroinc/oro-webpack-config-builder",
3
- "version": "5.1.0-alpha32",
3
+ "version": "5.1.0-alpha35",
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.16.10",
10
- "@babel/plugin-transform-runtime": "~7.16.10",
9
+ "@babel/core": "~7.17.9",
10
+ "@babel/plugin-transform-runtime": "~7.17.0",
11
11
  "@babel/preset-env": "~7.16.11",
12
12
  "autoprefixer": "~10.4.0",
13
13
  "babel-loader": "~8.2.3",
14
14
  "bindings": "~1.5.0",
15
- "css-loader": "~6.5.1",
15
+ "css-loader": "~6.7.1",
16
16
  "css-minimizer-webpack-plugin": "~3.4.1",
17
17
  "deepmerge": "~4.2.2",
18
18
  "exports-loader": "~3.1.0",
@@ -23,7 +23,7 @@
23
23
  "html-webpack-plugin": "~5.5.0",
24
24
  "imports-loader": "~3.1.1",
25
25
  "js-yaml": "~4.1.0",
26
- "mini-css-extract-plugin": "~2.5.2",
26
+ "mini-css-extract-plugin": "~2.6.0",
27
27
  "minimist": "~1.2.3",
28
28
  "nan": "~2.15.0",
29
29
  "path": "0.12.7",
@@ -32,14 +32,15 @@
32
32
  "printf": "~0.6.0",
33
33
  "resolve-url-loader": "^5.0.0",
34
34
  "rtlcss-webpack-plugin": "~4.0.6",
35
- "sass": "~1.49.0",
36
- "sass-loader": "~12.4.0",
35
+ "sass": "~1.50.0",
36
+ "sass-loader": "~12.6.0",
37
+ "schema-utils": "^4.0.0",
37
38
  "style-loader": "~3.3.1",
38
- "terser": "~5.10.0",
39
+ "terser": "~5.12.1",
39
40
  "text-loader": "0.0.1",
40
41
  "underscore": "~1.13.1",
41
42
  "url-loader": "~4.1.1",
42
- "webpack": "~5.66.0",
43
+ "webpack": "~5.72.0",
43
44
  "webpack-bundle-analyzer": "~4.5.0",
44
45
  "webpack-cli": "~4.9.1",
45
46
  "webpack-dev-server": "^4.7.3",
@@ -0,0 +1,25 @@
1
+ const {once} = require('underscore');
2
+
3
+ class AfterWebpackLogsPlugin {
4
+ /**
5
+ * @param {function} afterLogsCb
6
+ */
7
+ constructor(afterLogsCb) {
8
+ if (typeof afterLogsCb !== 'function') {
9
+ throw new Error('The "afterLogsCb" arg should be a function');
10
+ }
11
+ this._afterLogsCb = once(afterLogsCb);
12
+ }
13
+
14
+ apply(compiler) {
15
+ compiler.hooks.done.tap('AfterWebpackLogsPlugin', stats => {
16
+ const compilerStats = stats;
17
+ stats.compilation.hooks.statsPrinter.tap('AfterWebpackLogsPlugin', stats => {
18
+ // Making logs to be after all webpack logs in the console
19
+ setImmediate(() => this._afterLogsCb(compilerStats));
20
+ });
21
+ });
22
+ }
23
+ }
24
+
25
+ module.exports = AfterWebpackLogsPlugin;
@@ -9,8 +9,9 @@ class AdminStyleLoader extends StyleLoader {
9
9
  const {rtl_support: rtlSupport = false, styles: extraThemeConfig} =
10
10
  this._configLoader.loadConfig(themeName, 'Resources/public/themes/' + themeName + '/settings.yml');
11
11
  const baseThemeConfig = this._configLoader.loadConfig(themeName, 'Resources/config/oro/assets.yml');
12
+ const appRootExtraConfig = this._configLoader.loadConfig(themeName, 'config/oro/assets.yml');
12
13
  /** @type {Object.<string, ThemeGroupConfig>} */
13
- const themeConfig = merge(baseThemeConfig, extraThemeConfig);
14
+ const themeConfig = merge(baseThemeConfig, appRootExtraConfig, extraThemeConfig);
14
15
 
15
16
  return {
16
17
  themeConfig,