@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.
- package/js-to-scss/create-font-list.js +58 -0
- package/js-to-scss/index.js +5 -0
- package/modules-config/modules-config-loader.js +14 -3
- package/oro-webpack-config.js +65 -39
- package/package.json +1 -1
- package/validation/errors/theme-scheme-error.js +36 -0
- package/validation/index.js +5 -0
- package/validation/schemas/svg-icons-schema-full.js +1 -1
- package/validation/schemas/theme-schema-full.js +11 -0
- package/validation/schemas/theme-schema.js +83 -0
- package/validation/svg-icons-validator.js +3 -3
- package/validation/theme-validator.js +53 -0
|
@@ -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;
|
|
@@ -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
|
-
|
|
88
|
-
|
|
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
|
}
|
package/oro-webpack-config.js
CHANGED
|
@@ -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 {
|
|
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
|
@@ -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;
|
package/validation/index.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
+
});
|