@oroinc/oro-webpack-config-builder 0.0.3 → 4.2.1-dev11

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 CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  An integration of OroPlatform based applications with the Webpack.
4
4
 
5
- For more details see [the documentation](https://doc.oroinc.com/backend/bundles/platform/AssetBundle/).
5
+ For more details see [the documentation](https://doc.oroinc.com/bundles/platform/AssetBundle/).
@@ -1,15 +1,15 @@
1
1
  const path = require('path');
2
2
  const merge = require('deepmerge');
3
- const ThemesConfigLoader = require('./modules-config-loader');
3
+ const ModulesConfigLoader = require('./modules-config-loader');
4
4
 
5
- class LayoutModulesConfigLoader extends ThemesConfigLoader {
5
+ class LayoutModulesConfigLoader extends ModulesConfigLoader {
6
6
  /**
7
- * {@inheritdoc}
7
+ * @inheritdoc
8
8
  */
9
9
  loadConfig(theme, filePath) {
10
10
  let themeConfig = super.loadConfig(theme, path.join('/Resources/views/layouts/', theme, filePath));
11
11
  // recursive process parent theme
12
- const parentTheme = this.themes[theme];
12
+ const {parent: parentTheme} = this.themes[theme];
13
13
  if (typeof parentTheme === 'string') {
14
14
  const parentThemeConfig = this.loadConfig(parentTheme, filePath);
15
15
  themeConfig = merge(parentThemeConfig, themeConfig);
@@ -3,6 +3,9 @@ const fs = require('fs');
3
3
  const merge = require('deepmerge');
4
4
  const yaml = require('js-yaml');
5
5
 
6
+ // merge only unique items
7
+ const arrayMerge = (target, source) => target.concat(source.filter(item => !target.includes(item)));
8
+
6
9
  class ModulesConfigLoader {
7
10
  /**
8
11
  * @returns {Array}
@@ -43,21 +46,15 @@ class ModulesConfigLoader {
43
46
  if (!fs.existsSync(source)) return;
44
47
 
45
48
  fs.readdirSync(source).forEach(name => {
46
- const theme = path.resolve(source, name);
47
- if (!fs.lstatSync(theme).isDirectory()) {
49
+ const themePath = path.resolve(source, name);
50
+ if (!fs.lstatSync(themePath).isDirectory()) {
48
51
  return;
49
52
  }
50
- const themeFile = path.resolve(theme, themeInfoFileName);
53
+ const themeFile = path.resolve(themePath, themeInfoFileName);
51
54
  if (!fs.existsSync(themeFile)) return;
52
55
 
53
- if (!(name in themes)) {
54
- themes[name] = null;
55
- }
56
- const themeInfo = yaml.safeLoad(fs.readFileSync(themeFile, 'utf8'));
57
-
58
- if ('parent' in themeInfo) {
59
- themes[name] = themeInfo.parent;
60
- }
56
+ const theme = yaml.load(fs.readFileSync(themeFile, 'utf8'));
57
+ themes[name] = merge(themes[name] || {}, theme, {arrayMerge});
61
58
  });
62
59
  });
63
60
 
@@ -75,7 +72,7 @@ class ModulesConfigLoader {
75
72
  const absolutePath = bundle + filePath;
76
73
  if (!fs.existsSync(absolutePath)) return;
77
74
 
78
- const doc = yaml.safeLoad(fs.readFileSync(absolutePath, 'utf8'));
75
+ const doc = yaml.load(fs.readFileSync(absolutePath, 'utf8'));
79
76
  configs = merge(configs, doc);
80
77
  });
81
78
  return configs;
@@ -19,8 +19,9 @@ const ThemeConfigFactory = require('./theme-config-factory');
19
19
  const path = require('path');
20
20
  const prepareModulesMap = require('./plugin/map/prepare-modules-map');
21
21
  const resolve = require('enhanced-resolve');
22
- const webpackMerge = require('webpack-merge');
22
+ const {merge: webpackMerge} = require('webpack-merge');
23
23
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
24
+ const WebpackRTLPlugin = require('webpack-rtl-plugin');
24
25
 
25
26
  class ConfigBuilder {
26
27
  constructor() {
@@ -145,7 +146,7 @@ class ConfigBuilder {
145
146
  if (this._defaultLayoutThemes) {
146
147
  themes = [...themes, ...this._defaultLayoutThemes];
147
148
  } else {
148
- themes = [...themes, ...Object.keys(this._layoutModulesConfigLoader.themes)];
149
+ themes = [...themes, ...this._appConfig['themes']];
149
150
  }
150
151
  } else if (this._layoutThemes.indexOf(selectedTheme) !== -1) {
151
152
  // build single layout theme
@@ -244,9 +245,12 @@ class ConfigBuilder {
244
245
  }, {
245
246
  loader: 'sass-loader',
246
247
  options: {
247
- includePaths: [
248
- resolvedPublicPath + '/bundles'
249
- ],
248
+ sassOptions: {
249
+ includePaths: [
250
+ path.join(resolvedPublicPath, '/bundles')
251
+ ],
252
+ outputStyle: 'expanded'
253
+ },
250
254
  sourceMap: true
251
255
  }
252
256
  }]
@@ -337,11 +341,11 @@ class ConfigBuilder {
337
341
 
338
342
  plugins: [
339
343
  new OptimizeCssAssetsPlugin({
340
- cssProcessorOptions: {
341
- discardComments: {
342
- removeAll: true
343
- },
344
- zindex: false
344
+ cssProcessorPluginOptions: {
345
+ preset: ['default', {
346
+ // preserve comments for WebpackRTLPlugin, comments will be removed anyway later
347
+ discardComments: false,
348
+ }],
345
349
  }
346
350
  })
347
351
  ]
@@ -351,17 +355,21 @@ class ConfigBuilder {
351
355
  const webpackConfigs = [];
352
356
 
353
357
  themes.forEach(theme => {
358
+ let themeDefinition;
354
359
  let themeConfig;
355
360
  let buildPublicPath;
356
361
  if (this._isAdminTheme(theme)) {
362
+ themeDefinition = this._modulesConfigLoader.themes[theme.split('.')[1]];
357
363
  buildPublicPath = '/build/admin/';
358
364
  themeConfig = this._themeConfigFactory
359
365
  .create(theme, buildPublicPath, '/Resources/config/jsmodules.yml');
360
366
  } else {
367
+ themeDefinition = this._layoutModulesConfigLoader.themes[theme];
361
368
  buildPublicPath = `/build/${theme}/`;
362
369
  themeConfig = this._layoutThemeConfigFactory
363
370
  .create(theme, buildPublicPath, '/config/jsmodules.yml');
364
371
  }
372
+ const {rtl_support: rtlSupport = false} = themeDefinition;
365
373
  const resolvedBuildPath = path.join(resolvedPublicPath, buildPublicPath);
366
374
 
367
375
  const resolverConfig = {
@@ -379,6 +387,15 @@ class ConfigBuilder {
379
387
  return moduleName => resolver({}, '', moduleName, {});
380
388
  })(resolve.create.sync({...resolverConfig}));
381
389
 
390
+ const plugins = [];
391
+ if (rtlSupport && !env.skipCSS && !env.skipRTL) {
392
+ plugins.push(new WebpackRTLPlugin({
393
+ filename: '[name].rtl.css',
394
+ // RTL all chunks, except those that already support RTL
395
+ test: '(?<!(-rtl-ready))\\.css'
396
+ }));
397
+ }
398
+
382
399
  const cssEntryPoints = !env.skipCSS ? this._getCssEntryPoints(theme, buildPublicPath) : {};
383
400
  const jsEntryPoints = !env.skipJS ? themeConfig.entry : {};
384
401
 
@@ -400,6 +417,7 @@ class ConfigBuilder {
400
417
  new MapModulesPlugin(prepareModulesMap(resolver, themeConfig.map))
401
418
  ]
402
419
  },
420
+ plugins,
403
421
  module: {
404
422
  rules: [
405
423
  {
package/package.json CHANGED
@@ -1,45 +1,46 @@
1
1
  {
2
2
  "name": "@oroinc/oro-webpack-config-builder",
3
- "version": "0.0.3",
3
+ "version": "4.2.1-dev11",
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.6.1",
13
- "babel-loader": "^8.1.0",
9
+ "@babel/core": "^7.16.0",
10
+ "@babel/plugin-transform-runtime": "^7.16.4",
11
+ "@babel/preset-env": "^7.16.4",
12
+ "autoprefixer": "^9.8.8",
13
+ "babel-loader": "^8.2.3",
14
14
  "css-loader": "^3.6.0",
15
- "deepmerge": "3.3.0",
15
+ "deepmerge": "4.2.2",
16
16
  "exports-loader": "^0.7.0",
17
17
  "expose-loader": "^0.7.5",
18
18
  "extract-loader": "3.1.0",
19
19
  "file-loader": "^4.3.0",
20
20
  "font-awesome": "4.7.0",
21
21
  "happypack": "^5.0.1",
22
- "html-webpack-plugin": "^3.2.0",
23
22
  "imports-loader": "^0.8.0",
24
23
  "inject-loader": "^4.0.1",
25
- "js-yaml": "3.13.1",
24
+ "js-yaml": "4.1.0",
26
25
  "mini-css-extract-plugin": "0.7.0",
27
26
  "minimist": "^1.2.3",
28
- "node-sass": "^4.14.1",
27
+ "node-sass": "^6.0.1",
29
28
  "optimize-css-assets-webpack-plugin": "5.0.3",
30
29
  "path": "0.12.7",
31
30
  "postcss-loader": "3.0.0",
32
- "printf": "^0.6.0",
33
- "sass-loader": "7.1.0",
31
+ "printf": "^0.6.1",
32
+ "sass-loader": "10.2.0",
34
33
  "style-loader": "^0.23.1",
35
34
  "terser": "4.1.2",
36
35
  "text-loader": "0.0.1",
37
- "underscore": "^1.10.2",
36
+ "underscore": "1.13.*",
38
37
  "url-loader": "2.0.1",
39
- "webpack": "^4.43.0",
40
- "webpack-bundle-analyzer": "^3.8.0",
38
+ "webpack": "^4.46.0",
39
+ "webpack-bundle-analyzer": "^4.5.0",
41
40
  "webpack-cli": "^3.3.12",
42
- "webpack-dev-server": "^3.11.0",
43
- "webpack-merge": "4.2.1"
41
+ "webpack-dev-server": "^4.5.0",
42
+ "webpack-merge": "5.8.0",
43
+ "webpack-rtl-plugin": "^2.0.0",
44
+ "wildcard": "^2.0.0"
44
45
  }
45
46
  }
@@ -1,4 +1,6 @@
1
1
  const path = require('path');
2
+ const wildcard = require('wildcard');
3
+ const _ = require('underscore')
2
4
 
3
5
  class LayoutStyleLoader {
4
6
  /**
@@ -17,21 +19,25 @@ class LayoutStyleLoader {
17
19
  */
18
20
  getThemeEntryPoints(theme, buildPath) {
19
21
  const entryPoints = {};
20
-
22
+ const {rtl_support: rtlSupport = false} = this._configLoader.themes[theme];
21
23
  const themeConfig = this._configLoader.loadConfig(theme, '/config/assets.yml');
24
+ const writingOptions = {};
22
25
 
23
- for (const key in themeConfig) {
24
- if (themeConfig.hasOwnProperty(key)) {
25
- const config = themeConfig[key];
26
- let inputs = this._overrideInputs(config.inputs);
27
- inputs = this._sortInputs(inputs);
28
- if (config.output === undefined) {
29
- throw new Error('"output" for "' + key + '" group in theme "' + theme + '" is not defined');
30
- }
31
- const entryPointName = config.output.replace(/\.[^/.]+$/, '');
32
- const filePath = path.join(buildPath, config.output);
33
- entryPoints[entryPointName] = this._entryPointFileWriter.write('./../../../', inputs, filePath);
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));
34
35
  }
36
+
37
+ const entryPointName = output.replace(/\.[^/.]+$/, '');
38
+ const filePath = path.join(buildPath, output);
39
+ entryPoints[entryPointName] =
40
+ this._entryPointFileWriter.write('./../../../', inputs, filePath, writingOptions);
35
41
  }
36
42
  return entryPoints;
37
43
  }
@@ -86,6 +92,25 @@ class LayoutStyleLoader {
86
92
  });
87
93
  return [...settingsInputs, ...variablesInputs, ...restInputs];
88
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);
113
+ }
89
114
  }
90
115
 
91
116
  module.exports = LayoutStyleLoader;
@@ -1,6 +1,6 @@
1
1
  class ThemeConfigFactory {
2
2
  /**
3
- * @param {ThemesConfigLoader} configLoader
3
+ * @param {ModulesConfigLoader} configLoader
4
4
  * @param {DynamicImportsFileWriter} dynamicImportsFileWriter
5
5
  * @param {AppModulesFileWriter} appModulesFileWriter
6
6
  * @param {ConfigsFileWriter} configsFileWriter
@@ -15,16 +15,22 @@ class SCSSEntryPointFileWriter {
15
15
  * @param {string} baseInputPath base path for input files
16
16
  * @param {Array} inputs List of inputs
17
17
  * @param {string} output Output file path
18
+ * @param {Object} options writing options
18
19
  * @returns {string} JS file path of an output file
19
20
  */
20
- write(baseInputPath, inputs, output) {
21
+ write(baseInputPath, inputs, output, {ignoreRTLInputs = []}) {
21
22
  let content = '';
22
23
  inputs.forEach(input => {
24
+ const ignoreRTL = ignoreRTLInputs.indexOf(input) !== -1;
23
25
  input = input.replace(/\.[^/.]+$/, '');
24
26
  // don't add the base path to global node modules,
25
27
  // e.g. '~bootstrap/scss/bootstrap'
26
28
  const basePath = input.startsWith('~') ? '': baseInputPath;
27
- content += '@import "'+ basePath + input + '";\n';
29
+ let importModule = `@import "${basePath}${input}";\n`;
30
+ if (ignoreRTL) {
31
+ importModule = `/*rtl:begin:ignore*/\n${importModule}/*rtl:end:ignore*/\n`;
32
+ }
33
+ content += importModule;
28
34
  });
29
35
  const scssFilepath = path.resolve(this._publicPath + output + '.scss');
30
36
  fs.mkdirSync(path.dirname(scssFilepath), {recursive: true});