@oroinc/oro-webpack-config-builder 6.0.0-lts05 → 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 +66 -47
- package/package.json +1 -2
- 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
- package/plugin/integrity/integrity-file-plugin.js +0 -42
|
@@ -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
|
@@ -9,8 +9,6 @@ const EntryPointFileWriter = require('./writer/scss-entry-point-file-writer');
|
|
|
9
9
|
const LayoutModulesConfigLoader = require('./modules-config/layout-modules-config-loader');
|
|
10
10
|
const LayoutStyleLoader = require('./style/layout-style-loader');
|
|
11
11
|
const MapModulesPlugin = require('./plugin/map/map-modules-plugin');
|
|
12
|
-
const IntegrityFilePlugin = require('./plugin/integrity/integrity-file-plugin');
|
|
13
|
-
const {SubresourceIntegrityPlugin} = require('webpack-subresource-integrity');
|
|
14
12
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
|
15
13
|
const ModulesConfigLoader = require('./modules-config/modules-config-loader');
|
|
16
14
|
const DynamicImportsFileWriter = require('./writer/dynamic-imports-file-writer');
|
|
@@ -30,6 +28,7 @@ const EventEmitter = require('events');
|
|
|
30
28
|
const ErrorHandler = require('./error-handler');
|
|
31
29
|
const SVGSprite = require('./svg-sprite');
|
|
32
30
|
const TerserPlugin = require('terser-webpack-plugin');
|
|
31
|
+
const jsToSCSS = require('./js-to-scss');
|
|
33
32
|
require('resolve-url-loader');
|
|
34
33
|
require('lezer-loader');
|
|
35
34
|
|
|
@@ -276,6 +275,7 @@ class ConfigBuilder {
|
|
|
276
275
|
|
|
277
276
|
const webpackConfig = {
|
|
278
277
|
watchOptions: {
|
|
278
|
+
followSymlinks: true,
|
|
279
279
|
aggregateTimeout: 200,
|
|
280
280
|
ignored: /[\/\\]node_modules[\/\\].*\.js$/
|
|
281
281
|
},
|
|
@@ -283,8 +283,7 @@ class ConfigBuilder {
|
|
|
283
283
|
output: {
|
|
284
284
|
filename: '[name].js',
|
|
285
285
|
// Because we use third party libraries 'chunkFilename' should include only [name]
|
|
286
|
-
chunkFilename: this._getVersionedPath('chunk/[name].js', this.assetVersion)
|
|
287
|
-
crossOriginLoading: "anonymous"
|
|
286
|
+
chunkFilename: this._getVersionedPath('chunk/[name].js', this.assetVersion)
|
|
288
287
|
},
|
|
289
288
|
devtool: !env.skipSourcemap && 'inline-cheap-module-source-map',
|
|
290
289
|
mode: 'development',
|
|
@@ -322,44 +321,6 @@ class ConfigBuilder {
|
|
|
322
321
|
/[\/\\]bundles[\/\\]\.*[\/\\]lib[\/\\](?!chaplin|bootstrap|jquery\.dialog).*\.js$/
|
|
323
322
|
],
|
|
324
323
|
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
324
|
{
|
|
364
325
|
test: /\.(eot|ttf|woff|woff2|cur|ico|svg|png|jpg|gif)$/,
|
|
365
326
|
loader: 'url-loader',
|
|
@@ -391,10 +352,6 @@ class ConfigBuilder {
|
|
|
391
352
|
new webpack.optimize.MinChunkSizePlugin({
|
|
392
353
|
minChunkSize: 30000 // Minimum number of characters
|
|
393
354
|
}),
|
|
394
|
-
new SubresourceIntegrityPlugin(),
|
|
395
|
-
new IntegrityFilePlugin({
|
|
396
|
-
publicPath: this.resolvedPublicPath
|
|
397
|
-
}),
|
|
398
355
|
new AfterWebpackLogsPlugin(
|
|
399
356
|
stats => this.emitter.emit('build:complete', stats)
|
|
400
357
|
)
|
|
@@ -509,7 +466,10 @@ class ConfigBuilder {
|
|
|
509
466
|
);
|
|
510
467
|
}
|
|
511
468
|
}
|
|
512
|
-
let {
|
|
469
|
+
let {
|
|
470
|
+
rtl_support: rtlSupport = false,
|
|
471
|
+
resolve_extra_paths: resolveExtraPaths = []
|
|
472
|
+
} = themeDefinition;
|
|
513
473
|
const resolvedBuildPath = path.join(this.resolvedPublicPath, buildPublicPath);
|
|
514
474
|
|
|
515
475
|
if (resolveExtraPaths.length) {
|
|
@@ -566,6 +526,45 @@ class ConfigBuilder {
|
|
|
566
526
|
plugins,
|
|
567
527
|
module: {
|
|
568
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
|
+
},
|
|
569
568
|
{
|
|
570
569
|
test: /[\/\\]configs\.json$/,
|
|
571
570
|
loader: 'config-loader',
|
|
@@ -580,6 +579,18 @@ class ConfigBuilder {
|
|
|
580
579
|
};
|
|
581
580
|
}
|
|
582
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
|
+
|
|
583
594
|
_initialize(args, env) {
|
|
584
595
|
const entryPointFileWriter = new EntryPointFileWriter(this._publicPath);
|
|
585
596
|
|
|
@@ -611,6 +622,14 @@ class ConfigBuilder {
|
|
|
611
622
|
this.resolvedProjectPath,
|
|
612
623
|
this._publicPath
|
|
613
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
|
+
}
|
|
614
633
|
this._layoutStyleLoader = new LayoutStyleLoader(this._layoutModulesConfigLoader, entryPointFileWriter);
|
|
615
634
|
this._layoutThemeConfigFactory = new ThemeConfigFactory(
|
|
616
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-
|
|
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.",
|
|
@@ -49,7 +49,6 @@
|
|
|
49
49
|
"webpack-cli": "~5.0.0",
|
|
50
50
|
"webpack-dev-server": "^4.11.1",
|
|
51
51
|
"webpack-merge": "~5.8.0",
|
|
52
|
-
"webpack-subresource-integrity": "^5.2.0-rc.1",
|
|
53
52
|
"wildcard": "~2.0.0"
|
|
54
53
|
}
|
|
55
54
|
}
|
|
@@ -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
|
+
});
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const { createHash } = require('crypto');
|
|
2
|
-
const { Compilation, sources } = require('webpack');
|
|
3
|
-
const fs = require('fs').promises;
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
class IntegrityFilePlugin {
|
|
7
|
-
constructor({fileName = 'integrity.json', publicPath = '', algorithm = 'sha384'} = {}) {
|
|
8
|
-
this.fileName = fileName;
|
|
9
|
-
this.publicPath = publicPath;
|
|
10
|
-
this.algorithm = algorithm;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
apply(compiler) {
|
|
14
|
-
compiler.hooks.thisCompilation.tap('IntegrityFilePlugin', (compilation) => {
|
|
15
|
-
compilation.hooks.processAssets.tapPromise(
|
|
16
|
-
{ name: 'SubresourceIntegrityPlugin', stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER },
|
|
17
|
-
async () => {
|
|
18
|
-
const integrityData = Object.fromEntries(
|
|
19
|
-
Object.entries(compilation.assets)
|
|
20
|
-
.filter(([assetName]) => assetName.endsWith('.css') || assetName.endsWith('.js'))
|
|
21
|
-
.map(([assetName, asset]) => {
|
|
22
|
-
const hash = createHash(this.algorithm).update(asset.source()).digest('base64');
|
|
23
|
-
const assetNormalized = assetName.split('?')[0];
|
|
24
|
-
|
|
25
|
-
return [
|
|
26
|
-
path.join(compiler.options.output.publicPath || '', assetNormalized),
|
|
27
|
-
`${this.algorithm}-${hash}`
|
|
28
|
-
];
|
|
29
|
-
})
|
|
30
|
-
);
|
|
31
|
-
const jsonData = JSON.stringify(integrityData, null, 2);
|
|
32
|
-
const outputPath = path.join(this.publicPath, compiler.options.output.publicPath, this.fileName);
|
|
33
|
-
|
|
34
|
-
await fs.writeFile(outputPath, jsonData, 'utf8');
|
|
35
|
-
compilation.emitAsset(outputPath, new sources.RawSource(jsonData));
|
|
36
|
-
}
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
module.exports = IntegrityFilePlugin;
|