@oroinc/oro-webpack-config-builder 6.0.0-dev001 → 6.0.0-lts01

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.
@@ -28,7 +28,7 @@ class LayoutModulesConfigLoader extends ModulesConfigLoader {
28
28
  * @param {string} fileExtName file extension
29
29
  * @return {Object} Merged Configs loaded from all the bundles Yaml files matched by dirPath
30
30
  */
31
- getFilesPaths(theme, dirPath = '/public', fileExtName = '.txt') {
31
+ getFilesPaths(theme, dirPath = '/public', fileExtName = '.svg') {
32
32
  let iconsPaths = super.getFilesPaths(theme, dirPath, fileExtName);
33
33
 
34
34
  // recursive process parent theme
@@ -40,9 +40,14 @@ class ModulesConfigLoader {
40
40
  * @param {Array} bundles Array of ordered symfony bundle paths
41
41
  * @param {string|Array} themesLocation Path inside the bundle, where to find the theme
42
42
  * @param {string} themeInfoFileName Yaml File name with theme info
43
+ * @param {string} projectPublicPath Symfony public directory path related to application root folder
44
+ * @param {string} publicPath Symfony public directory path related to application root folder
43
45
  */
44
- constructor(bundles, themesLocation, themeInfoFileName) {
46
+ constructor(bundles, themesLocation, themeInfoFileName, projectPublicPath, publicPath) {
45
47
  this._bundles = bundles;
48
+ this._projectPublicPath = projectPublicPath;
49
+ this._publicPath = publicPath;
50
+
46
51
  if (!Array.isArray(themesLocation)) {
47
52
  themesLocation = [themesLocation];
48
53
  }
@@ -126,25 +131,57 @@ class ModulesConfigLoader {
126
131
  * @param {string} fileExtName file extension
127
132
  * @return {Object} Merged Configs loaded from all the bundles Yaml files matched by dirPath
128
133
  */
129
- getFilesPaths(theme, dirPath = '/public', fileExtName = '.txt') {
130
- const result = [];
131
-
134
+ getFilesPaths(theme, dirPath = '/public', fileExtName = '.svg') {
132
135
  this._processedFiles = [];
133
- this._bundles.forEach(bundle => {
134
- const absolutePath = path.resolve(bundle, dirPath, `${theme}/svg-icons`);
136
+ const iconsFolder = 'svg-icons';
137
+ const {resolve_extra_paths: resolveExtraPaths = []} = this.themes[theme];
138
+ const logProcessedFile = pathToFile => this._processedFiles.push(pathToFile);
139
+ const collectIcons = pathToFile => {
140
+ const icons = [];
141
+ const files = fs.readdirSync(pathToFile);
142
+
143
+ for (const file of files) {
144
+ const fileExt = path.extname(file);
145
+
146
+ if (fileExt === fileExtName) {
147
+ icons.push(`${pathToFile}/${file}`);
148
+ }
149
+ }
135
150
 
136
- if (fs.existsSync(absolutePath)) {
137
- this._processedFiles.push(absolutePath);
151
+ return icons;
152
+ };
153
+ const result = [];
138
154
 
139
- const files = fs.readdirSync(absolutePath);
155
+ if (resolveExtraPaths.length) {
156
+ resolveExtraPaths.map(
157
+ extraPath => path.join(this._projectPublicPath, this._publicPath, extraPath)
158
+ ).forEach(extraPath => {
159
+ const findIcons = extraPath => {
160
+ const files = fs.readdirSync(extraPath);
161
+
162
+ for (const file of files) {
163
+ const innerFolder = path.join(extraPath, file);
164
+
165
+ if (fs.statSync(innerFolder).isDirectory()) {
166
+ if (file === iconsFolder) {
167
+ logProcessedFile(innerFolder);
168
+ result.push(...collectIcons(innerFolder));
169
+ } else {
170
+ findIcons(innerFolder);
171
+ }
172
+ }
173
+ }
174
+ };
175
+ findIcons(extraPath);
176
+ });
177
+ }
140
178
 
141
- for (const file of files) {
142
- const fileExt = path.extname(file);
179
+ this._bundles.forEach(bundle => {
180
+ const absolutePath = path.resolve(bundle, dirPath, `${theme}/${iconsFolder}`);
143
181
 
144
- if (fileExt === fileExtName) {
145
- result.push(`${absolutePath}/${file}`);
146
- }
147
- }
182
+ if (fs.existsSync(absolutePath)) {
183
+ logProcessedFile(absolutePath);
184
+ result.push(...collectIcons(absolutePath));
148
185
  }
149
186
  });
150
187
 
@@ -28,6 +28,7 @@ const EventEmitter = require('events');
28
28
  const ErrorHandler = require('./error-handler');
29
29
  const SVGSprite = require('./svg-sprite');
30
30
  require('resolve-url-loader');
31
+ require('lezer-loader');
31
32
 
32
33
  class ConfigBuilder {
33
34
  constructor() {
@@ -355,7 +356,7 @@ class ConfigBuilder {
355
356
  },
356
357
  {
357
358
  test: /\.grammar$/,
358
- use: 'lezer-loader',
359
+ use: 'lezer-loader'
359
360
  }
360
361
  ]
361
362
  },
@@ -566,7 +567,9 @@ class ConfigBuilder {
566
567
  this._modulesConfigLoader = new ModulesConfigLoader(
567
568
  this._appConfig.paths,
568
569
  ['/Resources/public/themes/', '/public/themes/admin/'],
569
- 'settings.yml'
570
+ 'settings.yml',
571
+ this.resolvedProjectPath,
572
+ this._publicPath
570
573
  );
571
574
  this._adminThemes = this._modulesConfigLoader.themeNames.map(themeName => 'admin.' + themeName);
572
575
  this._adminStyleLoader = new AdminStyleLoader(this._modulesConfigLoader, entryPointFileWriter);
@@ -580,7 +583,9 @@ class ConfigBuilder {
580
583
  this._layoutModulesConfigLoader = new LayoutModulesConfigLoader(
581
584
  this._appConfig.paths,
582
585
  ['/Resources/views/layouts/', '/templates/layouts/'],
583
- 'theme.yml'
586
+ 'theme.yml',
587
+ this.resolvedProjectPath,
588
+ this._publicPath
584
589
  );
585
590
  this._layoutStyleLoader = new LayoutStyleLoader(this._layoutModulesConfigLoader, entryPointFileWriter);
586
591
  this._layoutThemeConfigFactory = new ThemeConfigFactory(
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@oroinc/oro-webpack-config-builder",
3
- "version": "6.0.0-dev001",
3
+ "version": "6.0.0-lts01",
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.21.3",
9
+ "@babel/core": "~7.23.3",
10
10
  "@babel/plugin-transform-runtime": "~7.21.0",
11
11
  "@babel/preset-env": "~7.21.2",
12
12
  "autoprefixer": "~10.4.13",
13
13
  "babel-loader": "~9.1.0",
14
14
  "bindings": "~1.5.0",
15
- "css-loader": "^6.7.3",
15
+ "css-loader": "^6.8.1",
16
16
  "css-minimizer-webpack-plugin": "~5.0.0",
17
17
  "deepmerge": "~4.3.1",
18
18
  "exports-loader": "~4.0.0",
@@ -26,8 +26,8 @@
26
26
  "minimist": "~1.2.7",
27
27
  "nan": "~2.17.0",
28
28
  "path": "0.12.7",
29
- "postcss": "~8.4.23",
30
- "postcss-loader": "~7.2.4",
29
+ "postcss": "<8.4.33",
30
+ "postcss-loader": "~7.3.4",
31
31
  "printf": "~0.6.0",
32
32
  "resolve-url-loader": "^5.0.0",
33
33
  "rtlcss-webpack-plugin": "~4.0.6",
@@ -14,6 +14,7 @@ class SvgSprite {
14
14
  */
15
15
  constructor(configLoader, theme, publicPath, buildPublicPath) {
16
16
  this.SPRITE_NAME = 'theme-icons';
17
+ this.SPRITE_NAME_RTL = `${this.SPRITE_NAME}.rtl`;
17
18
  this._configLoader = configLoader;
18
19
  this._theme = theme;
19
20
  this._publicPath = publicPath;
@@ -22,6 +23,18 @@ class SvgSprite {
22
23
  this.optimize();
23
24
  }
24
25
 
26
+ /**
27
+ * Gets theme configuration's settings
28
+ * @returns {*}
29
+ */
30
+ get themeConfig() {
31
+ if (!this._theme) {
32
+ return {};
33
+ }
34
+
35
+ return this._configLoader._themes[this._theme];
36
+ }
37
+
25
38
  /**
26
39
  * Starting optimization
27
40
  */
@@ -45,9 +58,10 @@ class SvgSprite {
45
58
  svgPaths.forEach(icon => {
46
59
  const svgName = path.parse(icon).name;
47
60
 
48
- if (svgName === this.SPRITE_NAME) {
61
+ if (svgName === this.SPRITE_NAME || svgName === this.SPRITE_NAME_RTL) {
49
62
  throw new Error(
50
- `The "${this.SPRITE_NAME}" is a reserved word and cannot be used as a svg name for building sprite.`
63
+ `The "${this.SPRITE_NAME}" and "${this.SPRITE_NAME_RTL}" are a reserved words and
64
+ cannot be used as a svg name for building sprite.`
51
65
  );
52
66
  }
53
67
 
@@ -91,9 +105,71 @@ class SvgSprite {
91
105
  await Promise.all(
92
106
  files.map(file => this.optimizeFile(file, dirPath))
93
107
  );
94
- this.createSprite(fs.readdirSync(dirPath), dirPath);
108
+
109
+ const {filesList, rtlFilesList} = this.createSpriteMap(fs.readdirSync(dirPath));
110
+ this.createSprite(filesList, dirPath, `${this.SPRITE_NAME}.svg`);
111
+
112
+ if (rtlFilesList) {
113
+ this.createSprite(rtlFilesList, dirPath, `${this.SPRITE_NAME_RTL}.svg`);
114
+ }
115
+
116
+ this.createMetadata(fs.readdirSync(dirPath), dirPath);
117
+ }
118
+
119
+ /**
120
+ * @param {Array} files
121
+ @returns {{ filesList: {}, rtlFilesList: {}|undefined }}
122
+ */
123
+ createSpriteMap(files) {
124
+ const SVGFiles = files.filter(file => path.extname(file) === '.svg');
125
+ const {rtl_support: rtl} = this.themeConfig;
126
+ const result = {
127
+ filesList: SVGFiles.reduce((list, file) => {
128
+ list[file] = path.parse(file).name;
129
+ return list;
130
+ }, {})
131
+ };
132
+
133
+ if (!rtl) {
134
+ return result;
135
+ }
136
+
137
+ const LTR_TO_RTL = [
138
+ ['left', 'right'],
139
+ ['right', 'left'],
140
+ ['start', 'end'],
141
+ ['end', 'start']
142
+ ];
143
+ const rtlFiles = {...result.filesList};
144
+ const rtlRegexp = /-(?:left|right|start|end)\.svg$/;
145
+ const pairedFiles = Object.entries(rtlFiles).filter(([key])=> rtlRegexp.test(key));
146
+ const swappedFiles = pairedFiles.map(rtlFile => {
147
+ const file = rtlFile[1];
148
+ const pair = LTR_TO_RTL.find(pair => file.endsWith(pair[0]));
149
+
150
+ if (!pair) {
151
+ return rtlFile;
152
+ }
153
+
154
+ const RTLFileName = `${file.substring(0, file.length - pair[0].length)}${pair[1]}`;
155
+ const RTLPairFile = pairedFiles.find(rtlFile => rtlFile[1] === RTLFileName);
156
+
157
+ if (RTLPairFile) {
158
+ return [
159
+ rtlFile[0],
160
+ RTLPairFile[1]
161
+ ];
162
+ }
163
+
164
+ return rtlFile;
165
+ });
166
+
167
+ result.rtlFilesList = Object.assign(rtlFiles, Object.fromEntries(swappedFiles));
168
+
169
+ return result;
95
170
  }
96
171
 
172
+
97
173
  /**
98
174
  * Optimizing svg using svgo
99
175
  * @param {Object} file
@@ -117,18 +193,34 @@ class SvgSprite {
117
193
 
118
194
  /**
119
195
  * Creating sprite
120
- * @param {Array} files
196
+ * @param {Object} files
121
197
  * @param {string} dirPath
198
+ * @param {string} spriteName
122
199
  */
123
- createSprite(files, dirPath) {
200
+ createSprite(files, dirPath, spriteName) {
124
201
  const sprites = svgstore(svgSpriteConfig);
202
+ for (const [fullFileName, file] of Object.entries(files)) {
203
+ sprites.add(file, fs.readFileSync(path.join(dirPath, fullFileName), 'utf8'));
204
+ }
205
+ fs.writeFileSync(path.join(dirPath, spriteName), sprites.toString(), 'utf8');
206
+ }
125
207
 
126
- for (const file of files) {
208
+ /**
209
+ * @param {Array<string>} files
210
+ * @param {string} dirPath
211
+ */
212
+ createMetadata(files, dirPath) {
213
+ const SVGFiles = files.filter(file => path.extname(file) === '.svg');
214
+ const svgList = SVGFiles.reduce((acc, file) => {
127
215
  const svgName = path.parse(file).name;
128
- sprites.add(svgName, fs.readFileSync(path.join(dirPath, file), 'utf8'));
129
- }
216
+ acc[svgName] = file;
130
217
 
131
- fs.writeFileSync(path.join(dirPath, `${this.SPRITE_NAME}.svg`), sprites, 'utf8');
218
+ return acc;
219
+ }, {});
220
+
221
+ fs.writeFileSync(
222
+ path.join(dirPath, 'icons-metadata.json'), JSON.stringify({icons: svgList}, null, null)
223
+ );
132
224
  }
133
225
  }
134
226