@oroinc/oro-webpack-config-builder 5.1.0-alpha8 → 5.1.0-lts001
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/README.md +1 -1
- package/error-handler.js +95 -0
- package/loader/config-loader.js +1 -2
- package/loader/inject-loader/LICENSE.md +21 -0
- package/loader/inject-loader/README.md +54 -0
- package/loader/inject-loader/index.js +10 -0
- package/loader/inject-loader/injectify.js +66 -0
- package/loader/inject-loader/package.json +55 -0
- package/loader/inject-loader/wrapper_template.js +32 -0
- package/modules-config/layout-modules-config-loader.js +66 -2
- package/modules-config/modules-config-loader.js +52 -19
- package/oro-webpack-config.js +416 -301
- package/package.json +37 -37
- package/plugin/logs/after-webpack-logs-plugin.js +25 -0
- package/style/admin-style-loader.js +23 -0
- package/style/layout-style-loader.js +12 -109
- package/style/style-loader.js +156 -46
- package/theme-config-factory.js +45 -6
- package/utils.js +30 -0
- package/validation/assets-validator.js +104 -0
- package/validation/errors/assets-input-file-error.js +24 -0
- package/validation/errors/assets-schema-error.js +40 -0
- package/validation/errors/base-error.js +37 -0
- package/validation/errors/jsmodules-extra-modules-error.js +22 -0
- package/validation/errors/jsmodules-schema-error.js +40 -0
- package/validation/errors/styles-error.js +24 -0
- package/validation/index.js +36 -0
- package/validation/jsmodules-validator.js +53 -0
- package/validation/schema-validator.js +62 -0
- package/validation/schemas/assets-schema-full.js +22 -0
- package/validation/schemas/assets-schema.js +32 -0
- package/validation/schemas/jsmodules-schema-full.js +11 -0
- package/validation/schemas/jsmodules-schema.js +76 -0
- package/writer/configs-file-writer.js +1 -1
- package/writer/dynamic-imports-file-writer.js +3 -3
- package/writer/scss-entry-point-file-writer.js +1 -1
package/README.md
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
|
|
3
3
|
An integration of OroPlatform based applications with the Webpack.
|
|
4
4
|
|
|
5
|
-
For more details see [the documentation](https://doc.oroinc.com/
|
|
5
|
+
For more details see [the documentation](https://doc.oroinc.com/bundles/platform/AssetBundle/).
|
package/error-handler.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const BaseError = require('./validation/errors/base-error');
|
|
2
|
+
const {isVerboseMode} = require('./utils');
|
|
3
|
+
const {red, yellow, green, bgRed} = require('colorette');
|
|
4
|
+
|
|
5
|
+
const emptyLine = length => new Array(length).fill(' ').join('');
|
|
6
|
+
const multiline = (color, msg) => {
|
|
7
|
+
msg = ` ${msg}`;
|
|
8
|
+
msg = msg + emptyLine(120 - msg.length);
|
|
9
|
+
|
|
10
|
+
return `\n${color(emptyLine(msg.length))}\n${color(msg)}\n${color(emptyLine(msg.length))}`;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
class ErrorHandler {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.failedThemes = [];
|
|
16
|
+
|
|
17
|
+
process.on('beforeExit', code => {
|
|
18
|
+
this.onProcess();
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Show a message about failed themes
|
|
24
|
+
*/
|
|
25
|
+
onProcess() {
|
|
26
|
+
if (this.failedThemes.length) {
|
|
27
|
+
let msg = '[ERROR] Assets build';
|
|
28
|
+
|
|
29
|
+
if (this.failedThemes.length === 1) {
|
|
30
|
+
msg = `${msg} for "${this.failedThemes[0]}" theme failed.`;
|
|
31
|
+
} else {
|
|
32
|
+
msg = `${msg} for "${this.failedThemes.join(', ')}" themes failed.`;
|
|
33
|
+
}
|
|
34
|
+
console.error(multiline(bgRed, msg));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Executes when the Webpack Compiler will complete a compilation
|
|
41
|
+
* In case of empty Webpack Config this method will not run
|
|
42
|
+
*
|
|
43
|
+
* @param {Object} stats
|
|
44
|
+
*/
|
|
45
|
+
onBuildComplete(stats) {
|
|
46
|
+
const failed = stats?.compilation?.errors.length !== 0;
|
|
47
|
+
|
|
48
|
+
if (failed) {
|
|
49
|
+
console.error(multiline(bgRed, '[ERROR] Assets build failed.'));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {Array|string} errors
|
|
56
|
+
* @param {string} [failedTheme]
|
|
57
|
+
*/
|
|
58
|
+
displayError(errors, failedTheme) {
|
|
59
|
+
if (typeof failedTheme === 'string') {
|
|
60
|
+
this.failedThemes.push(failedTheme);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (typeof errors === 'string') {
|
|
64
|
+
errors = [errors];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const errorMessage = errors.map(err => {
|
|
68
|
+
const highlightPrefix = str => {
|
|
69
|
+
const prefix = str.substring(0, str.search(':'));
|
|
70
|
+
return red(prefix) + str.substring(str.search(':'));
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (err instanceof BaseError) {
|
|
74
|
+
err.message = `${err.message}\n${green(err.extra)}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let msg = err.message;
|
|
78
|
+
|
|
79
|
+
if (isVerboseMode()) {
|
|
80
|
+
msg = highlightPrefix(err.stack);
|
|
81
|
+
} else {
|
|
82
|
+
const command = yellow('php bin/console oro:assets:build --env=dev --verbose');
|
|
83
|
+
const info = `${green(`Run the command ${command} to see more information`)}`;
|
|
84
|
+
|
|
85
|
+
msg = highlightPrefix(`${err.name}: ${msg}\n${info}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return `${msg}\n`;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
console.error(errorMessage.join('\n'));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = ErrorHandler;
|
package/loader/config-loader.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const _loaderUtils = require('loader-utils');
|
|
3
2
|
|
|
4
3
|
module.exports = function(source) {
|
|
5
4
|
this.cacheable && this.cacheable();
|
|
6
|
-
const {resolver, relativeTo = ''} =
|
|
5
|
+
const {resolver, relativeTo = ''} = this.getOptions() || {};
|
|
7
6
|
|
|
8
7
|
if (typeof resolver !== 'function') {
|
|
9
8
|
return source;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014 Justin Morris
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h3>💉📦<br><br><code>inject-loader</code></h3>
|
|
3
|
+
<h4>A Webpack loader for injecting code into modules via their dependencies</h4>
|
|
4
|
+
<a href="https://travis-ci.org/plasticine/inject-loader"><img src="https://img.shields.io/travis/plasticine/inject-loader/master.svg?style=flat-square" alt="build status" /></a> <a href="https://www.npmjs.com/package/inject-loader"><img src="https://img.shields.io/npm/v/inject-loader.svg?style=flat-square" alt="npm version" /></a> <a href="https://www.npmjs.com/package/inject-loader"><img src="https://img.shields.io/npm/dm/inject-loader.svg?style=flat-square" alt="npm downloads" /></a>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
***
|
|
8
|
+
|
|
9
|
+
### Why
|
|
10
|
+
|
|
11
|
+
This is particularly useful for writing tests where mocking things inside your module-under-test is sometimes necessary before execution.
|
|
12
|
+
|
|
13
|
+
`inject-loader` was inspired by, and builds upon ideas introduced in [jauco/webpack-injectable](https://github.com/jauco/webpack-injectable).
|
|
14
|
+
|
|
15
|
+
### Usage
|
|
16
|
+
|
|
17
|
+
[Documentation: Using loaders](http://webpack.github.io/docs/using-loaders.html)
|
|
18
|
+
|
|
19
|
+
Use the inject loader by adding the `inject-loader!` [inline loader](https://webpack.js.org/concepts/loaders/#inline) when you use `require`, this will return a function that can used in test code to modify the injected module.
|
|
20
|
+
|
|
21
|
+
By default all `require` statements in an injected module will be altered to be replaced with an injector, though if a replacement it not specified the default values will be used.
|
|
22
|
+
|
|
23
|
+
### Examples
|
|
24
|
+
|
|
25
|
+
Given some code in a module like this:
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
// MyStore.js
|
|
29
|
+
|
|
30
|
+
var Dispatcher = require('lib/dispatcher');
|
|
31
|
+
var EventEmitter = require('events').EventEmitter;
|
|
32
|
+
var handleAction = require('lib/handle_action');
|
|
33
|
+
|
|
34
|
+
Dispatcher.register(handleAction, 'MyStore');
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
You can manipulate it’s dependencies when you come to write tests as follows:
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
// If no flags are provided when using the loader then
|
|
41
|
+
// all require statements will be wrapped in an injector
|
|
42
|
+
MyModuleInjector = require('inject-loader!MyStore')
|
|
43
|
+
MyModule = MyModuleInjector({
|
|
44
|
+
'lib/dispatcher': DispatcherMock,
|
|
45
|
+
'events': EventsMock,
|
|
46
|
+
'lib/handle_action': HandleActionMock
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
There are a few examples of complete test setups for both Webpack 1, 2, 3 & 4 in the [`example`](./example) folder.
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const injectify = require('./injectify.js');
|
|
2
|
+
|
|
3
|
+
module.exports = function injectifyLoader(source, inputSourceMap) {
|
|
4
|
+
if (this.cacheable) {
|
|
5
|
+
this.cacheable();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const {code, map} = injectify(this, source, inputSourceMap);
|
|
9
|
+
this.callback(null, code, map);
|
|
10
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const {transform, traverse, types: t, transformFromAst} = require('@babel/core');
|
|
2
|
+
const wrapperTemplate = require('./wrapper_template.js');
|
|
3
|
+
|
|
4
|
+
function processRequireCall(path) {
|
|
5
|
+
const dependencyString = path.node.arguments[0].value;
|
|
6
|
+
path.replaceWith(
|
|
7
|
+
t.expressionStatement(
|
|
8
|
+
t.conditionalExpression(
|
|
9
|
+
t.callExpression(
|
|
10
|
+
t.memberExpression(t.identifier('__injections'), t.identifier('hasOwnProperty'), false),
|
|
11
|
+
[t.stringLiteral(dependencyString)]
|
|
12
|
+
),
|
|
13
|
+
t.memberExpression(t.identifier('__injections'), t.stringLiteral(dependencyString), true),
|
|
14
|
+
path.node
|
|
15
|
+
)
|
|
16
|
+
)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return dependencyString;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = function injectify(context, source, inputSourceMap) {
|
|
23
|
+
const {ast} = transform(source, {
|
|
24
|
+
babelrc: false,
|
|
25
|
+
code: false,
|
|
26
|
+
compact: false,
|
|
27
|
+
ast: true,
|
|
28
|
+
filename: context.resourcePath,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const dependencies = [];
|
|
32
|
+
traverse(ast, {
|
|
33
|
+
CallExpression(path) {
|
|
34
|
+
if (t.isIdentifier(path.node.callee, {name: 'require'})) {
|
|
35
|
+
dependencies.push(processRequireCall(path));
|
|
36
|
+
path.skip();
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (dependencies.length === 0) {
|
|
42
|
+
context.emitWarning(
|
|
43
|
+
"The module you are trying to inject into doesn't have any dependencies. " +
|
|
44
|
+
'Are you sure you want to do this?'
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const wrapperModuleAst = t.file(
|
|
49
|
+
t.program([
|
|
50
|
+
wrapperTemplate({
|
|
51
|
+
SOURCE: t.blockStatement(ast.program.body),
|
|
52
|
+
SOURCE_PATH: t.stringLiteral(context.resourcePath),
|
|
53
|
+
DEPENDENCIES: t.arrayExpression(dependencies.map(d => t.stringLiteral(d))),
|
|
54
|
+
}),
|
|
55
|
+
])
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return transformFromAst(wrapperModuleAst, source, {
|
|
59
|
+
sourceMaps: context.sourceMap,
|
|
60
|
+
sourceFileName: context.resourcePath,
|
|
61
|
+
inputSourceMap,
|
|
62
|
+
babelrc: false,
|
|
63
|
+
compact: false,
|
|
64
|
+
filename: context.resourcePath,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "inject-loader",
|
|
3
|
+
"version": "4.0.1",
|
|
4
|
+
"description": "A Webpack loader for injecting code into modules via their dependencies",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "webpack --config config/webpack.config.js",
|
|
8
|
+
"build:test": "webpack --config config/webpack.test.config.js",
|
|
9
|
+
"build:release": "yarn run build && mkdir -p ./dist && cp -f ./tmp/index.js ./dist/index.js && cp -f ./tmp/index.js.map ./dist/index.js.map",
|
|
10
|
+
"pretest:unit": "yarn build && yarn build:test",
|
|
11
|
+
"test:unit": "mocha tmp/testBundle.js --require source-map-support/register",
|
|
12
|
+
"test:integration": "./script/integration_test",
|
|
13
|
+
"test": "flow && yarn test:unit && yarn test:integration",
|
|
14
|
+
"precommit": "pretty-quick --staged"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"*.md",
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"author": "Justin Morris <desk@pixelbloom.com> (http://pixelbloom.com)",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git@github.com:plasticine/inject-loader.git"
|
|
24
|
+
},
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"babel-core": "~6"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"babel-loader": "^7.1.4",
|
|
31
|
+
"babel-plugin-add-module-exports": "^0.2.1",
|
|
32
|
+
"babel-plugin-transform-flow-strip-types": "^6.22.0",
|
|
33
|
+
"babel-preset-es2015": "^6.22.0",
|
|
34
|
+
"flow-bin": "^0.69.0",
|
|
35
|
+
"husky": "^0.14.3",
|
|
36
|
+
"mocha": "^5.0.5",
|
|
37
|
+
"prettier": "^1.11.1",
|
|
38
|
+
"pretty-quick": "^1.4.1",
|
|
39
|
+
"source-map-support": "^0.5.4",
|
|
40
|
+
"webpack": "^4.35.0",
|
|
41
|
+
"webpack-cli": "^3.3.5"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"webpack": "^1 || ^2 || ^3 || ^4"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"webpack",
|
|
48
|
+
"testing",
|
|
49
|
+
"loader",
|
|
50
|
+
"webpack-loader",
|
|
51
|
+
"inject",
|
|
52
|
+
"mock",
|
|
53
|
+
"mocking"
|
|
54
|
+
]
|
|
55
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const {template} = require('@babel/core');
|
|
2
|
+
|
|
3
|
+
module.exports = template(`
|
|
4
|
+
module.exports = function __injector(__injections) {
|
|
5
|
+
__injections = __injections || {};
|
|
6
|
+
(function __validateInjection() {
|
|
7
|
+
var validDependencies = DEPENDENCIES;
|
|
8
|
+
var injectedDependencies = Object.keys(__injections);
|
|
9
|
+
var invalidInjectedDependencies = injectedDependencies.filter(function (dependency) {
|
|
10
|
+
return validDependencies.indexOf(dependency) === -1;
|
|
11
|
+
});
|
|
12
|
+
if (invalidInjectedDependencies.length > 0) {
|
|
13
|
+
var validDependenciesString = ' - ' + validDependencies.join('\\n - ');
|
|
14
|
+
var injectedDependenciesString = ' - ' + injectedDependencies.join('\\n - ');
|
|
15
|
+
var invalidDependenciesString = ' - ' + invalidInjectedDependencies.join('\\n - ');
|
|
16
|
+
throw new Error('Injection Error in ' + SOURCE_PATH + '\\n\\n' +
|
|
17
|
+
'The following injections are invalid:\\n' + invalidDependenciesString + '\\n\\n' +
|
|
18
|
+
'The following injections were passed in:\\n' + injectedDependenciesString + '\\n\\n' +
|
|
19
|
+
'Valid injection targets for this module are:\\n' + validDependenciesString + '\\n'
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
})();
|
|
23
|
+
__injector.sourcePath = SOURCE_PATH;
|
|
24
|
+
__injector.validDependencies = DEPENDENCIES;
|
|
25
|
+
var module = { exports: {} };
|
|
26
|
+
var exports = module.exports;
|
|
27
|
+
(function () {
|
|
28
|
+
SOURCE
|
|
29
|
+
})();
|
|
30
|
+
return module.exports;
|
|
31
|
+
}
|
|
32
|
+
`);
|
|
@@ -4,19 +4,83 @@ const ModulesConfigLoader = require('./modules-config-loader');
|
|
|
4
4
|
|
|
5
5
|
class LayoutModulesConfigLoader extends ModulesConfigLoader {
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* @inheritdoc
|
|
8
8
|
*/
|
|
9
9
|
loadConfig(theme, filePath) {
|
|
10
10
|
let themeConfig = super.loadConfig(theme, path.join('Resources/views/layouts/', theme, filePath));
|
|
11
|
+
themeConfig = merge(themeConfig, super.loadConfig(theme, path.join('templates/layouts/', theme, filePath)));
|
|
11
12
|
// recursive process parent theme
|
|
12
13
|
const {parent: parentTheme} = this.themes[theme];
|
|
13
14
|
if (typeof parentTheme === 'string') {
|
|
15
|
+
const processedFiles = this.processedFiles;
|
|
14
16
|
const parentThemeConfig = this.loadConfig(parentTheme, filePath);
|
|
17
|
+
|
|
15
18
|
themeConfig = merge(parentThemeConfig, themeConfig);
|
|
19
|
+
// processedFiles from parent theme is added to processedFiles of current theme
|
|
20
|
+
this._processedFiles = [...processedFiles, ...this.processedFiles];
|
|
16
21
|
}
|
|
17
|
-
|
|
18
22
|
return themeConfig;
|
|
19
23
|
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* All build names:
|
|
27
|
+
* - based on theme names
|
|
28
|
+
* - and theme's extra js builds
|
|
29
|
+
*
|
|
30
|
+
* @return {string[]}
|
|
31
|
+
*/
|
|
32
|
+
get buildNames() {
|
|
33
|
+
const buildNames = super.buildNames;
|
|
34
|
+
this.themeNames.forEach(theme => {
|
|
35
|
+
buildNames.push(...this.extraJSBuildNames(theme));
|
|
36
|
+
});
|
|
37
|
+
return buildNames;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Collect extra js-build names for a theme
|
|
42
|
+
*
|
|
43
|
+
* @param {string} theme name on the theme
|
|
44
|
+
* @return {string[]}
|
|
45
|
+
*/
|
|
46
|
+
extraJSBuildNames(theme) {
|
|
47
|
+
const {extra_js_builds: extraJSBuilds = []} = this._themes[theme] || {};
|
|
48
|
+
return [...extraJSBuilds.map(suffix => `${theme}-${suffix}`)];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if buildName is an extra js build of layout theme
|
|
53
|
+
*
|
|
54
|
+
* @param {string} buildName name of the build
|
|
55
|
+
* @return {boolean}
|
|
56
|
+
*/
|
|
57
|
+
isExtraJSBuild(buildName) {
|
|
58
|
+
const [, suffix] = this.splitBuildName(buildName);
|
|
59
|
+
return suffix !== void 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Splits build name on parts theme name and suffix (name of extra js build)
|
|
64
|
+
* @param {string} buildName
|
|
65
|
+
* @return {[string]|[string, string]}
|
|
66
|
+
*/
|
|
67
|
+
splitBuildName(buildName) {
|
|
68
|
+
// suffix can not contain '-'
|
|
69
|
+
const marches = buildName.match(/(.+)-([^\-]+)?/);
|
|
70
|
+
const result = [];
|
|
71
|
+
if (marches) {
|
|
72
|
+
const [, theme, suffix] = marches;
|
|
73
|
+
const {extra_js_builds: extraJSBuilds = []} = this._themes[theme] || {};
|
|
74
|
+
if (extraJSBuilds.includes(suffix)) {
|
|
75
|
+
result.push(marches[1], marches[2]);
|
|
76
|
+
} else {
|
|
77
|
+
result.push(buildName);
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
result.push(buildName);
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
20
84
|
}
|
|
21
85
|
|
|
22
86
|
module.exports = LayoutModulesConfigLoader;
|
|
@@ -2,6 +2,10 @@ const path = require('path');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const merge = require('deepmerge');
|
|
4
4
|
const yaml = require('js-yaml');
|
|
5
|
+
const validation = require('../validation');
|
|
6
|
+
|
|
7
|
+
// merge only unique items
|
|
8
|
+
const arrayMerge = (target, source) => target.concat(source.filter(item => !target.includes(item)));
|
|
5
9
|
|
|
6
10
|
class ModulesConfigLoader {
|
|
7
11
|
/**
|
|
@@ -18,60 +22,83 @@ class ModulesConfigLoader {
|
|
|
18
22
|
return Object.keys(this._themes);
|
|
19
23
|
}
|
|
20
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @returns {Array}
|
|
27
|
+
*/
|
|
28
|
+
get buildNames() {
|
|
29
|
+
return this.themeNames;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @returns {Array}
|
|
34
|
+
*/
|
|
35
|
+
get processedFiles() {
|
|
36
|
+
return [...this._processedFiles];
|
|
37
|
+
}
|
|
38
|
+
|
|
21
39
|
/**
|
|
22
40
|
* @param {Array} bundles Array of ordered symfony bundle paths
|
|
23
|
-
* @param {string} themesLocation Path inside the bundle, where to find the theme
|
|
24
|
-
* @param {string} themeInfoFileName
|
|
41
|
+
* @param {string|Array} themesLocation Path inside the bundle, where to find the theme
|
|
42
|
+
* @param {string} themeInfoFileName Yaml File name with theme info
|
|
25
43
|
*/
|
|
26
44
|
constructor(bundles, themesLocation, themeInfoFileName) {
|
|
27
45
|
this._bundles = bundles;
|
|
28
|
-
|
|
46
|
+
if (!Array.isArray(themesLocation)) {
|
|
47
|
+
themesLocation = [themesLocation];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const themes = {};
|
|
51
|
+
const self = this;
|
|
52
|
+
themesLocation.forEach(themesLocation => {
|
|
53
|
+
self._collectThemes(themes, themesLocation, themeInfoFileName);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
this._themes = themes;
|
|
57
|
+
this._processedFiles = [];
|
|
29
58
|
}
|
|
30
59
|
|
|
31
60
|
/**
|
|
32
|
-
*
|
|
61
|
+
* Collects list of themes with their parents into given storage(themes param)
|
|
62
|
+
*
|
|
63
|
+
* @param {Object} themes
|
|
33
64
|
* @param {string} themesLocation
|
|
34
65
|
* @param {string} themeInfoFileName
|
|
35
|
-
* @returns {Object.<string|null>}
|
|
36
66
|
* @private
|
|
37
67
|
*/
|
|
38
|
-
|
|
39
|
-
const themes = {};
|
|
68
|
+
_collectThemes(themes, themesLocation, themeInfoFileName) {
|
|
40
69
|
this._bundles.forEach(bundle => {
|
|
41
70
|
const source = bundle + themesLocation;
|
|
42
71
|
|
|
43
72
|
if (!fs.existsSync(source)) return;
|
|
44
73
|
|
|
45
74
|
fs.readdirSync(source).forEach(name => {
|
|
46
|
-
const
|
|
47
|
-
if (!fs.lstatSync(
|
|
75
|
+
const themePath = path.resolve(source, name);
|
|
76
|
+
if (!fs.lstatSync(themePath).isDirectory()) {
|
|
48
77
|
return;
|
|
49
78
|
}
|
|
50
|
-
const themeFile = path.resolve(
|
|
79
|
+
const themeFile = path.resolve(themePath, themeInfoFileName);
|
|
51
80
|
if (!fs.existsSync(themeFile)) return;
|
|
52
81
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
themes[name] = yaml.load(fs.readFileSync(themeFile, 'utf8'));
|
|
82
|
+
const theme = yaml.load(fs.readFileSync(themeFile, 'utf8'));
|
|
83
|
+
themes[name] = merge(themes[name] || {}, theme, {arrayMerge});
|
|
57
84
|
});
|
|
58
85
|
});
|
|
59
|
-
|
|
60
|
-
return themes;
|
|
61
86
|
}
|
|
62
87
|
|
|
63
88
|
/**
|
|
64
89
|
* @param {string} theme Theme name
|
|
65
90
|
* @param {string|string[]} filePath Path (or paths with fallback) to the file inside bundle directory where to find the configs
|
|
66
|
-
* @return {Object} Merged Configs loaded from all the bundles
|
|
91
|
+
* @return {Object} Merged Configs loaded from all the bundles Yaml files matched by filePath
|
|
67
92
|
*/
|
|
68
93
|
loadConfig(theme, filePath) {
|
|
69
94
|
let configs = {};
|
|
70
95
|
const filePaths = [].concat(filePath);
|
|
96
|
+
|
|
97
|
+
this._processedFiles = [];
|
|
71
98
|
this._bundles.forEach(bundle => {
|
|
72
99
|
let absolutePath;
|
|
73
100
|
|
|
74
|
-
for (
|
|
101
|
+
for (const file of filePaths) {
|
|
75
102
|
absolutePath = path.resolve(bundle, file);
|
|
76
103
|
if (fs.existsSync(absolutePath)) {
|
|
77
104
|
break;
|
|
@@ -80,10 +107,16 @@ class ModulesConfigLoader {
|
|
|
80
107
|
}
|
|
81
108
|
|
|
82
109
|
if (absolutePath) {
|
|
83
|
-
const
|
|
110
|
+
const rawDoc = fs.readFileSync(absolutePath, 'utf8');
|
|
111
|
+
const doc = yaml.load(rawDoc);
|
|
112
|
+
|
|
113
|
+
this._processedFiles.push(absolutePath);
|
|
114
|
+
|
|
115
|
+
validation.checkSchema(absolutePath, doc, theme);
|
|
84
116
|
configs = merge(configs, doc);
|
|
85
117
|
}
|
|
86
118
|
});
|
|
119
|
+
|
|
87
120
|
return configs;
|
|
88
121
|
}
|
|
89
122
|
}
|