@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.
- 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
|
@@ -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 {
|
|
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
|
@@ -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
|
+
});
|