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

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
@@ -337,11 +338,11 @@ class ConfigBuilder {
337
338
 
338
339
  plugins: [
339
340
  new OptimizeCssAssetsPlugin({
340
- cssProcessorOptions: {
341
- discardComments: {
342
- removeAll: true
343
- },
344
- zindex: false
341
+ cssProcessorPluginOptions: {
342
+ preset: ['default', {
343
+ // preserve comments for WebpackRTLPlugin, comments will be removed anyway later
344
+ discardComments: false,
345
+ }],
345
346
  }
346
347
  })
347
348
  ]
@@ -351,17 +352,21 @@ class ConfigBuilder {
351
352
  const webpackConfigs = [];
352
353
 
353
354
  themes.forEach(theme => {
355
+ let themeDefinition;
354
356
  let themeConfig;
355
357
  let buildPublicPath;
356
358
  if (this._isAdminTheme(theme)) {
359
+ themeDefinition = this._modulesConfigLoader.themes[theme.split('.')[1]];
357
360
  buildPublicPath = '/build/admin/';
358
361
  themeConfig = this._themeConfigFactory
359
362
  .create(theme, buildPublicPath, '/Resources/config/jsmodules.yml');
360
363
  } else {
364
+ themeDefinition = this._layoutModulesConfigLoader.themes[theme];
361
365
  buildPublicPath = `/build/${theme}/`;
362
366
  themeConfig = this._layoutThemeConfigFactory
363
367
  .create(theme, buildPublicPath, '/config/jsmodules.yml');
364
368
  }
369
+ const {rtl_support: rtlSupport = false} = themeDefinition;
365
370
  const resolvedBuildPath = path.join(resolvedPublicPath, buildPublicPath);
366
371
 
367
372
  const resolverConfig = {
@@ -379,6 +384,15 @@ class ConfigBuilder {
379
384
  return moduleName => resolver({}, '', moduleName, {});
380
385
  })(resolve.create.sync({...resolverConfig}));
381
386
 
387
+ const plugins = [];
388
+ if (rtlSupport && !env.skipCSS && !env.skipRTL) {
389
+ plugins.push(new WebpackRTLPlugin({
390
+ filename: '[name].rtl.css',
391
+ // RTL all chunks, except those that already support RTL
392
+ test: '(?<!(-rtl-ready))\\.css'
393
+ }));
394
+ }
395
+
382
396
  const cssEntryPoints = !env.skipCSS ? this._getCssEntryPoints(theme, buildPublicPath) : {};
383
397
  const jsEntryPoints = !env.skipJS ? themeConfig.entry : {};
384
398
 
@@ -400,6 +414,7 @@ class ConfigBuilder {
400
414
  new MapModulesPlugin(prepareModulesMap(resolver, themeConfig.map))
401
415
  ]
402
416
  },
417
+ plugins,
403
418
  module: {
404
419
  rules: [
405
420
  {
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-dev4",
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",
31
+ "printf": "^0.6.1",
33
32
  "sass-loader": "7.1.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",
41
- "webpack-cli": "^3.3.12",
42
- "webpack-dev-server": "^3.11.0",
43
- "webpack-merge": "4.2.1"
38
+ "webpack": "^4.46.0",
39
+ "webpack-bundle-analyzer": "^4.5.0",
40
+ "webpack-cli": "^4.9.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});