@oroinc/oro-webpack-config-builder 6.0.0-lts05 → 6.0.0-lts07

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,58 @@
1
+ /**
2
+ * Converts JS object to SCSS list variable for theme fonts
3
+ * @example {
4
+ * main: {
5
+ * family: 'Jakarta',
6
+ * variants: [{
7
+ * path: '/font.woff2',
8
+ * style: 'normal',
9
+ * weight: '700'
10
+ * }]
11
+ * formats: ['woff2']
12
+ * }
13
+ * } => (
14
+ * 'main': (
15
+ * 'family': 'Jakarta',
16
+ * 'variants': (
17
+ * (
18
+ * 'path': '/font.woff2',
19
+ * 'style': 'normal',
20
+ * 'weight': '700',
21
+ * )
22
+ * )
23
+ * 'formats': ('woff2')
24
+ * )
25
+ * )
26
+ *
27
+ * @param {object} fonts
28
+ * @param {string }scssVarName
29
+ * @returns {string}
30
+ */
31
+ const fontObjToSCSS = ({fonts, scssVarName = 'global-theme-fonts'} = {}) => {
32
+ if (!Object.keys(fonts).length) {
33
+ return `$${scssVarName}: ()`;
34
+ }
35
+ const fontsVariantsVars = Object.entries(fonts).map(([name, font]) => {
36
+ return `
37
+ '${name}': (
38
+ 'family': '${font.family}',
39
+ 'variants': (
40
+ ${font.variants.map(item => `(
41
+ 'path': '${item.path}',
42
+ ${item.weight !== void 0 ? `'weight': ${item.weight},` : ''}
43
+ ${item.style !== void 0 ? `'style': ${item.style}` : ''}
44
+ )`).join(`, `)}
45
+ ),
46
+ 'formats': (${font.formats.map(format => `'${format}'`).join(', ')})
47
+ )
48
+ `;
49
+ });
50
+
51
+ return `
52
+ $${scssVarName}: (
53
+ ${fontsVariantsVars.join(`, `)}
54
+ );
55
+ `;
56
+ };
57
+
58
+ module.exports = fontObjToSCSS;
@@ -0,0 +1,5 @@
1
+ const createFontList = require('./create-font-list');
2
+
3
+ module.exports = {
4
+ createFontList
5
+ };
@@ -71,6 +71,8 @@ class ModulesConfigLoader {
71
71
  * @private
72
72
  */
73
73
  _collectThemes(themes, themesLocation, themeInfoFileName) {
74
+ this._processedFiles = [];
75
+
74
76
  this._bundles.forEach(bundle => {
75
77
  const source = bundle + themesLocation;
76
78
 
@@ -81,11 +83,20 @@ class ModulesConfigLoader {
81
83
  if (!fs.lstatSync(themePath).isDirectory()) {
82
84
  return;
83
85
  }
86
+
84
87
  const themeFile = path.resolve(themePath, themeInfoFileName);
85
- if (!fs.existsSync(themeFile)) return;
86
88
 
87
- const theme = yaml.load(fs.readFileSync(themeFile, 'utf8'));
88
- themes[name] = merge(themes[name] || {}, theme, {arrayMerge});
89
+ if (!fs.existsSync(themeFile)) {
90
+ return;
91
+ }
92
+
93
+ this._processedFiles.push(themeFile);
94
+
95
+ const doc = yaml.load(fs.readFileSync(themeFile, 'utf8'));
96
+
97
+ validation.checkSchema(themeFile, doc, name);
98
+
99
+ themes[name] = merge(themes[name] || {}, doc, {arrayMerge});
89
100
  });
90
101
  });
91
102
  }
@@ -30,6 +30,7 @@ const EventEmitter = require('events');
30
30
  const ErrorHandler = require('./error-handler');
31
31
  const SVGSprite = require('./svg-sprite');
32
32
  const TerserPlugin = require('terser-webpack-plugin');
33
+ const jsToSCSS = require('./js-to-scss');
33
34
  require('resolve-url-loader');
34
35
  require('lezer-loader');
35
36
 
@@ -276,6 +277,7 @@ class ConfigBuilder {
276
277
 
277
278
  const webpackConfig = {
278
279
  watchOptions: {
280
+ followSymlinks: true,
279
281
  aggregateTimeout: 200,
280
282
  ignored: /[\/\\]node_modules[\/\\].*\.js$/
281
283
  },
@@ -322,44 +324,6 @@ class ConfigBuilder {
322
324
  /[\/\\]bundles[\/\\]\.*[\/\\]lib[\/\\](?!chaplin|bootstrap|jquery\.dialog).*\.js$/
323
325
  ],
324
326
  rules: [
325
- {
326
- test: /\.s?css$/,
327
- use: [{
328
- loader: args.hot ? 'style-loader' : MiniCssExtractPlugin.loader
329
- }, {
330
- loader: 'css-loader',
331
- options: {
332
- importLoaders: 1,
333
- sourceMap: true,
334
- // can't use esModule since resolve-url-loader needs file path to start with '~'
335
- esModule: false
336
- }
337
- }, {
338
- loader: 'resolve-url-loader'
339
- }, {
340
- loader: 'postcss-loader',
341
- options: {
342
- sourceMap: true,
343
- postcssOptions: {
344
- plugins: [
345
- require('autoprefixer')
346
- ]
347
- }
348
- }
349
- }, {
350
- loader: 'sass-loader',
351
- options: {
352
- implementation: require('sass'),
353
- sassOptions: {
354
- includePaths: [
355
- path.join(this.resolvedPublicPath, '/bundles')
356
- ],
357
- outputStyle: 'expanded'
358
- },
359
- sourceMap: true
360
- }
361
- }]
362
- },
363
327
  {
364
328
  test: /\.(eot|ttf|woff|woff2|cur|ico|svg|png|jpg|gif)$/,
365
329
  loader: 'url-loader',
@@ -509,7 +473,10 @@ class ConfigBuilder {
509
473
  );
510
474
  }
511
475
  }
512
- let {rtl_support: rtlSupport = false, resolve_extra_paths: resolveExtraPaths = []} = themeDefinition;
476
+ let {
477
+ rtl_support: rtlSupport = false,
478
+ resolve_extra_paths: resolveExtraPaths = []
479
+ } = themeDefinition;
513
480
  const resolvedBuildPath = path.join(this.resolvedPublicPath, buildPublicPath);
514
481
 
515
482
  if (resolveExtraPaths.length) {
@@ -566,6 +533,45 @@ class ConfigBuilder {
566
533
  plugins,
567
534
  module: {
568
535
  rules: [
536
+ {
537
+ test: /\.s?css$/,
538
+ use: [{
539
+ loader: args.hot ? 'style-loader' : MiniCssExtractPlugin.loader
540
+ }, {
541
+ loader: 'css-loader',
542
+ options: {
543
+ importLoaders: 1,
544
+ sourceMap: true,
545
+ // can't use esModule since resolve-url-loader needs file path to start with '~'
546
+ esModule: false
547
+ }
548
+ }, {
549
+ loader: 'resolve-url-loader'
550
+ }, {
551
+ loader: 'postcss-loader',
552
+ options: {
553
+ sourceMap: true,
554
+ postcssOptions: {
555
+ plugins: [
556
+ require('autoprefixer')
557
+ ]
558
+ }
559
+ }
560
+ }, {
561
+ loader: 'sass-loader',
562
+ options: {
563
+ implementation: require('sass'),
564
+ sassOptions: {
565
+ includePaths: [
566
+ path.join(this.resolvedPublicPath, '/bundles')
567
+ ],
568
+ outputStyle: 'expanded'
569
+ },
570
+ sourceMap: true,
571
+ additionalData: this.prepareAdditionalSCSS.bind(this, themeDefinition)
572
+ }
573
+ }]
574
+ },
569
575
  {
570
576
  test: /[\/\\]configs\.json$/,
571
577
  loader: 'config-loader',
@@ -580,6 +586,18 @@ class ConfigBuilder {
580
586
  };
581
587
  }
582
588
 
589
+ prepareAdditionalSCSS(theme, content, loaderContext) {
590
+ if (typeof theme.fonts === 'object') {
591
+ const fonts = {};
592
+ for (const [name, {family, formats, variants}] of Object.entries(theme.fonts)) {
593
+ fonts[name] = {family, formats, variants};
594
+ }
595
+ return jsToSCSS.createFontList({fonts}) + content;
596
+ }
597
+
598
+ return content;
599
+ }
600
+
583
601
  _initialize(args, env) {
584
602
  const entryPointFileWriter = new EntryPointFileWriter(this._publicPath);
585
603
 
@@ -611,6 +629,14 @@ class ConfigBuilder {
611
629
  this.resolvedProjectPath,
612
630
  this._publicPath
613
631
  );
632
+
633
+ for (const [theme, config] of Object.entries(this._layoutModulesConfigLoader.themes)) {
634
+ validation.themeValidator.checkFullSchema(
635
+ config,
636
+ this._layoutModulesConfigLoader.processedFiles,
637
+ theme
638
+ );
639
+ }
614
640
  this._layoutStyleLoader = new LayoutStyleLoader(this._layoutModulesConfigLoader, entryPointFileWriter);
615
641
  this._layoutThemeConfigFactory = new ThemeConfigFactory(
616
642
  this._layoutModulesConfigLoader,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oroinc/oro-webpack-config-builder",
3
- "version": "6.0.0-lts05",
3
+ "version": "6.0.0-lts07",
4
4
  "author": "Oro, Inc. (https://oroinc.com)",
5
5
  "license": "MIT",
6
6
  "description": "An integration of OroPlatform based applications with the Webpack.",
@@ -0,0 +1,36 @@
1
+ const BaseError = require('./base-error');
2
+
3
+ class SvgIconsSchemaError extends BaseError {
4
+ /**
5
+ * @example
6
+ * Invalid Theme config: the "theme.yml" files in the "default" theme do not match the API schema.
7
+ * has an unknown property 'rest'.
8
+ * at default/theme.yml
9
+ *
10
+ * @example
11
+ * Invalid Theme config: the "theme.yml" files in the "default" theme do not match the API schema.
12
+ * misses the property 'label'.
13
+ * List of processed files for the "default" theme:
14
+ * /default/theme.yml
15
+ * /default/theme.yml
16
+ *
17
+ * @param {string} reason
18
+ * @param {Array} files
19
+ * @param {string} theme
20
+ */
21
+ constructor(reason, files, theme) {
22
+ let msg = `the "theme.yml" files in the "${theme}" theme do not match the API schema.\n${reason}`;
23
+
24
+ const filesListMsg = BaseError.generateFilesListMsg(files, theme);
25
+
26
+ if (filesListMsg.length) {
27
+ msg = `${msg}\n${filesListMsg}`;
28
+ }
29
+
30
+ super(msg);
31
+
32
+ this.name = 'Invalid Theme config';
33
+ }
34
+ }
35
+
36
+ module.exports = SvgIconsSchemaError;
@@ -1,15 +1,18 @@
1
1
  const assetsValidation = require('./assets-validator');
2
2
  const jsmodulesValidator = require('./jsmodules-validator');
3
3
  const svgIconsValidator = require('./svg-icons-validator');
4
+ const themeValidator = require('./theme-validator');
4
5
 
5
6
  const isJSModulesPath = path => /jsmodules([-a-zA-Z\d]*)\.yml$/.test(path);
6
7
  const isAssetsPath = path => /assets\.yml$/.test(path);
7
8
  const isSvgIconsPath = path => /svg-icons\.yml$/.test(path);
9
+ const isThemePath = path => /theme\.yml$/.test(path);
8
10
 
9
11
  module.exports = {
10
12
  assetsValidation,
11
13
  jsmodulesValidator,
12
14
  svgIconsValidator,
15
+ themeValidator,
13
16
 
14
17
  /**
15
18
  * set public path for all validators
@@ -36,6 +39,8 @@ module.exports = {
36
39
  }
37
40
  } else if (isSvgIconsPath(filePath)) {
38
41
  svgIconsValidator.checkSchema(filePath, doc, theme);
42
+ } else if (isThemePath(filePath)) {
43
+ themeValidator.checkSchema(filePath, doc, theme);
39
44
  }
40
45
  }
41
46
  };
@@ -10,7 +10,7 @@ module.exports = {
10
10
  type: 'object',
11
11
  properties: {
12
12
  exclude: {
13
- description: 'The "exclude" property is an array of svg files to exclude from svg sprite.',
13
+ description: 'The "exclude" property is an array of svg files to exclude from svg sprite.',
14
14
  type: 'array',
15
15
  minItems: 0
16
16
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Schema to validate theme.yml files
3
+ *
4
+ * @description
5
+ * Full scheme complements "theme-schema" one.
6
+ * It should have only rules which are not defined in "theme-schema" schema due to avoid duplicates in error messages
7
+ */
8
+ module.exports = {
9
+ type: 'object',
10
+ required: ['label']
11
+ };
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Schema to validate theme.yml files
3
+ */
4
+ module.exports = {
5
+ type: 'object',
6
+ properties: {
7
+ label: {
8
+ type: 'string'
9
+ },
10
+ description: {
11
+ type: 'string'
12
+ },
13
+ groups: {
14
+ type: 'array',
15
+ items: {
16
+ type: 'string'
17
+ }
18
+ },
19
+ icon: {
20
+ type: 'string'
21
+ },
22
+ logo: {
23
+ type: 'string'
24
+ },
25
+ logo_small: {
26
+ type: 'string'
27
+ },
28
+ rtl_support: {
29
+ type: 'boolean'
30
+ },
31
+ svg_icons_support: {
32
+ type: 'boolean'
33
+ },
34
+ fonts: {
35
+ type: 'object',
36
+ patternProperties: {
37
+ '.*': {
38
+ type: 'object',
39
+ required: ['family', 'variants', 'formats'],
40
+ additionalProperties: false,
41
+ properties: {
42
+ family: {
43
+ type: 'string'
44
+ },
45
+ variants: {
46
+ type: 'array',
47
+ items: {
48
+ type: 'object',
49
+ required: ['path'],
50
+ additionalProperties: false,
51
+ properties: {
52
+ path: {
53
+ type: 'string'
54
+ },
55
+ weight: {
56
+ anyOf: [{
57
+ type: 'string'
58
+ }, {
59
+ type: 'integer'
60
+ }]
61
+ },
62
+ style: {
63
+ type: 'string'
64
+ }
65
+ }
66
+ }
67
+ },
68
+ formats: {
69
+ type: 'array',
70
+ items: {
71
+ type: 'string'
72
+ }
73
+ },
74
+ preload: {
75
+ type: 'boolean'
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ };
83
+
@@ -4,7 +4,7 @@ const schemaValidator = require('./schema-validator');
4
4
  const {isProdMode} = require('../utils');
5
5
  const EventEmitter = require('events');
6
6
  const emitter = new EventEmitter();
7
- const SvgIsonsSchemaError = require('./errors/svg-icons-schema-error');
7
+ const SvgIconsSchemaError = require('./errors/svg-icons-schema-error');
8
8
 
9
9
  module.exports = Object.assign({}, schemaValidator, {
10
10
  emitter,
@@ -22,7 +22,7 @@ module.exports = Object.assign({}, schemaValidator, {
22
22
  const result = this.validateSchema(schema, doc);
23
23
 
24
24
  if (!result.valid) {
25
- const error = new SvgIsonsSchemaError(result.formattedError, [filePath], theme);
25
+ const error = new SvgIconsSchemaError(result.formattedError, [filePath], theme);
26
26
 
27
27
  this.emitter.emit('error', error);
28
28
  }
@@ -43,7 +43,7 @@ module.exports = Object.assign({}, schemaValidator, {
43
43
  const result = this.validateSchema(fullSchema, doc);
44
44
 
45
45
  if (!result.valid) {
46
- const error = new SvgIsonsSchemaError(result.formattedError, files, theme);
46
+ const error = new SvgIconsSchemaError(result.formattedError, files, theme);
47
47
 
48
48
  this.emitter.emit('error', error);
49
49
  }
@@ -0,0 +1,53 @@
1
+ const schema = require('./schemas/theme-schema');
2
+ const fullSchema = require('./schemas/theme-schema-full');
3
+ const schemaValidator = require('./schema-validator');
4
+ const {isProdMode} = require('../utils');
5
+ const EventEmitter = require('events');
6
+ const emitter = new EventEmitter();
7
+ const ThemeSchemaError = require('./errors/theme-scheme-error');
8
+
9
+ module.exports = Object.assign({}, schemaValidator, {
10
+ emitter,
11
+
12
+ /**
13
+ * @param {string} filePath
14
+ * @param {Object} doc
15
+ * @param {string} theme
16
+ * @returns {boolean|undefined}
17
+ */
18
+ checkSchema(filePath, doc, theme) {
19
+ if (isProdMode()) {
20
+ return;
21
+ }
22
+ const result = this.validateSchema(schema, doc);
23
+
24
+ if (!result.valid) {
25
+ const error = new ThemeSchemaError(result.formattedError, [filePath], theme);
26
+
27
+ this.emitter.emit('error', error);
28
+ }
29
+
30
+ return result.valid;
31
+ },
32
+
33
+ /**
34
+ * @param {Object} doc
35
+ * @param {Array} files
36
+ * @param {string} theme
37
+ * @returns {boolean|undefined}
38
+ */
39
+ checkFullSchema(doc, files = [], theme) {
40
+ if (isProdMode()) {
41
+ return;
42
+ }
43
+ const result = this.validateSchema(fullSchema, doc);
44
+
45
+ if (!result.valid) {
46
+ const error = new ThemeSchemaError(result.formattedError, files, theme);
47
+
48
+ this.emitter.emit('error', error);
49
+ }
50
+
51
+ return result.valid;
52
+ }
53
+ });