@oroinc/oro-webpack-config-builder 6.0.0-lts04 → 6.0.0-lts06

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
  }
@@ -28,6 +28,7 @@ const EventEmitter = require('events');
28
28
  const ErrorHandler = require('./error-handler');
29
29
  const SVGSprite = require('./svg-sprite');
30
30
  const TerserPlugin = require('terser-webpack-plugin');
31
+ const jsToSCSS = require('./js-to-scss');
31
32
  require('resolve-url-loader');
32
33
  require('lezer-loader');
33
34
 
@@ -274,6 +275,7 @@ class ConfigBuilder {
274
275
 
275
276
  const webpackConfig = {
276
277
  watchOptions: {
278
+ followSymlinks: true,
277
279
  aggregateTimeout: 200,
278
280
  ignored: /[\/\\]node_modules[\/\\].*\.js$/
279
281
  },
@@ -319,44 +321,6 @@ class ConfigBuilder {
319
321
  /[\/\\]bundles[\/\\]\.*[\/\\]lib[\/\\](?!chaplin|bootstrap|jquery\.dialog).*\.js$/
320
322
  ],
321
323
  rules: [
322
- {
323
- test: /\.s?css$/,
324
- use: [{
325
- loader: args.hot ? 'style-loader' : MiniCssExtractPlugin.loader
326
- }, {
327
- loader: 'css-loader',
328
- options: {
329
- importLoaders: 1,
330
- sourceMap: true,
331
- // can't use esModule since resolve-url-loader needs file path to start with '~'
332
- esModule: false
333
- }
334
- }, {
335
- loader: 'resolve-url-loader'
336
- }, {
337
- loader: 'postcss-loader',
338
- options: {
339
- sourceMap: true,
340
- postcssOptions: {
341
- plugins: [
342
- require('autoprefixer')
343
- ]
344
- }
345
- }
346
- }, {
347
- loader: 'sass-loader',
348
- options: {
349
- implementation: require('sass'),
350
- sassOptions: {
351
- includePaths: [
352
- path.join(this.resolvedPublicPath, '/bundles')
353
- ],
354
- outputStyle: 'expanded'
355
- },
356
- sourceMap: true
357
- }
358
- }]
359
- },
360
324
  {
361
325
  test: /\.(eot|ttf|woff|woff2|cur|ico|svg|png|jpg|gif)$/,
362
326
  loader: 'url-loader',
@@ -502,7 +466,10 @@ class ConfigBuilder {
502
466
  );
503
467
  }
504
468
  }
505
- let {rtl_support: rtlSupport = false, resolve_extra_paths: resolveExtraPaths = []} = themeDefinition;
469
+ let {
470
+ rtl_support: rtlSupport = false,
471
+ resolve_extra_paths: resolveExtraPaths = []
472
+ } = themeDefinition;
506
473
  const resolvedBuildPath = path.join(this.resolvedPublicPath, buildPublicPath);
507
474
 
508
475
  if (resolveExtraPaths.length) {
@@ -559,6 +526,45 @@ class ConfigBuilder {
559
526
  plugins,
560
527
  module: {
561
528
  rules: [
529
+ {
530
+ test: /\.s?css$/,
531
+ use: [{
532
+ loader: args.hot ? 'style-loader' : MiniCssExtractPlugin.loader
533
+ }, {
534
+ loader: 'css-loader',
535
+ options: {
536
+ importLoaders: 1,
537
+ sourceMap: true,
538
+ // can't use esModule since resolve-url-loader needs file path to start with '~'
539
+ esModule: false
540
+ }
541
+ }, {
542
+ loader: 'resolve-url-loader'
543
+ }, {
544
+ loader: 'postcss-loader',
545
+ options: {
546
+ sourceMap: true,
547
+ postcssOptions: {
548
+ plugins: [
549
+ require('autoprefixer')
550
+ ]
551
+ }
552
+ }
553
+ }, {
554
+ loader: 'sass-loader',
555
+ options: {
556
+ implementation: require('sass'),
557
+ sassOptions: {
558
+ includePaths: [
559
+ path.join(this.resolvedPublicPath, '/bundles')
560
+ ],
561
+ outputStyle: 'expanded'
562
+ },
563
+ sourceMap: true,
564
+ additionalData: this.prepareAdditionalSCSS.bind(this, themeDefinition)
565
+ }
566
+ }]
567
+ },
562
568
  {
563
569
  test: /[\/\\]configs\.json$/,
564
570
  loader: 'config-loader',
@@ -573,6 +579,18 @@ class ConfigBuilder {
573
579
  };
574
580
  }
575
581
 
582
+ prepareAdditionalSCSS(theme, content, loaderContext) {
583
+ if (typeof theme.fonts === 'object') {
584
+ const fonts = {};
585
+ for (const [name, {family, formats, variants}] of Object.entries(theme.fonts)) {
586
+ fonts[name] = {family, formats, variants};
587
+ }
588
+ return jsToSCSS.createFontList({fonts}) + content;
589
+ }
590
+
591
+ return content;
592
+ }
593
+
576
594
  _initialize(args, env) {
577
595
  const entryPointFileWriter = new EntryPointFileWriter(this._publicPath);
578
596
 
@@ -604,6 +622,14 @@ class ConfigBuilder {
604
622
  this.resolvedProjectPath,
605
623
  this._publicPath
606
624
  );
625
+
626
+ for (const [theme, config] of Object.entries(this._layoutModulesConfigLoader.themes)) {
627
+ validation.themeValidator.checkFullSchema(
628
+ config,
629
+ this._layoutModulesConfigLoader.processedFiles,
630
+ theme
631
+ );
632
+ }
607
633
  this._layoutStyleLoader = new LayoutStyleLoader(this._layoutModulesConfigLoader, entryPointFileWriter);
608
634
  this._layoutThemeConfigFactory = new ThemeConfigFactory(
609
635
  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-lts04",
3
+ "version": "6.0.0-lts06",
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
+ });