@oroinc/oro-webpack-config-builder 5.1.0-alpha4 → 5.1.0-alpha40
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/inject-loader/index.js +1 -0
- package/loader/inject-loader/index.js.map +1 -0
- package/modules-config/layout-modules-config-loader.js +67 -3
- package/modules-config/modules-config-loader.js +65 -22
- package/oro-webpack-config.js +423 -298
- package/package.json +39 -35
- package/plugin/logs/after-webpack-logs-plugin.js +25 -0
- package/prepare-modules-shim.js +13 -1
- 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;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports=function(e){var n={};function t(i){if(n[i])return n[i].exports;var r=n[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,t),r.l=!0,r.exports}return t.m=e,t.c=n,t.d=function(e,n,i){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:i})},t.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t.w={},t(t.s=3)}([function(e,n){e.exports=require("babel-core")},function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var i=t(0);n.default=(0,i.template)("\n module.exports = function __injector(__injections) {\n __injections = __injections || {};\n\n (function __validateInjection() {\n var validDependencies = DEPENDENCIES;\n var injectedDependencies = Object.keys(__injections);\n var invalidInjectedDependencies = injectedDependencies.filter(function (dependency) {\n return validDependencies.indexOf(dependency) === -1;\n });\n\n if (invalidInjectedDependencies.length > 0) {\n var validDependenciesString = ' - ' + validDependencies.join('\\n - ');\n var injectedDependenciesString = ' - ' + injectedDependencies.join('\\n - ');\n var invalidDependenciesString = ' - ' + invalidInjectedDependencies.join('\\n - ');\n\n throw new Error('Injection Error in ' + SOURCE_PATH + '\\n\\n' +\n 'The following injections are invalid:\\n' + invalidDependenciesString + '\\n\\n' +\n 'The following injections were passed in:\\n' + injectedDependenciesString + '\\n\\n' +\n 'Valid injection targets for this module are:\\n' + validDependenciesString + '\\n'\n );\n }\n })();\n\n __injector.sourcePath = SOURCE_PATH;\n __injector.validDependencies = DEPENDENCIES;\n\n var module = { exports: {} };\n var exports = module.exports;\n\n (function () {\n SOURCE\n })();\n\n return module.exports;\n }\n"),e.exports=n.default},function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=function(e,n,t){var i=(0,r.transform)(n,{babelrc:!1,code:!1,compact:!1,filename:e.resourcePath}).ast,o=[];(0,r.traverse)(i,{CallExpression:function(e){r.types.isIdentifier(e.node.callee,{name:"require"})&&(o.push(function(e){var n=e.node.arguments[0].value;return e.replaceWith(r.types.expressionStatement(r.types.conditionalExpression(r.types.callExpression(r.types.memberExpression(r.types.identifier("__injections"),r.types.identifier("hasOwnProperty"),!1),[r.types.stringLiteral(n)]),r.types.memberExpression(r.types.identifier("__injections"),r.types.stringLiteral(n),!0),e.node))),n}(e)),e.skip())}}),0===o.length&&e.emitWarning("The module you are trying to inject into doesn't have any dependencies. Are you sure you want to do this?");var a=r.types.file(r.types.program([(0,s.default)({SOURCE:i,SOURCE_PATH:r.types.stringLiteral(e.resourcePath),DEPENDENCIES:r.types.arrayExpression(o.map(function(e){return r.types.stringLiteral(e)}))})]));return(0,r.transformFromAst)(a,n,{sourceMaps:e.sourceMap,sourceFileName:e.resourcePath,inputSourceMap:t,babelrc:!1,compact:!1,filename:e.resourcePath})};var i,r=t(0),o=t(1),s=(i=o)&&i.__esModule?i:{default:i};e.exports=n.default},function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=function(e,n){this.cacheable&&this.cacheable();var t=(0,o.default)(this,e,n),i=t.code,r=t.map;this.callback(null,i,r)};var i,r=t(2),o=(i=r)&&i.__esModule?i:{default:i};e.exports=n.default}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["webpack:///webpack/bootstrap 393a9525777b8b817cc3","webpack:///external \"babel-core\"","webpack:///src/index.js","webpack:///src/injectify.js","webpack:///src/wrapper_template.js"],"sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 1);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 393a9525777b8b817cc3","module.exports = require(\"babel-core\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"babel-core\"\n// module id = 0\n// module chunks = 0","// @flow\n\nimport injectify from './injectify.js';\n\nexport default function injectifyLoader(source, inputSourceMap) {\n if (this.cacheable) {\n this.cacheable();\n }\n\n const { code, map } = injectify(this, source, inputSourceMap);\n this.callback(null, code, map);\n}\n\n\n\n// WEBPACK FOOTER //\n// src/index.js","// @flow\n\nimport { transform, traverse, types as t, transformFromAst } from 'babel-core';\nimport wrapperTemplate from './wrapper_template.js';\n\nfunction processRequireCall(path) {\n const dependencyString = path.node.arguments[0].value;\n path.replaceWith(\n t.expressionStatement(\n t.conditionalExpression(\n t.callExpression(\n t.memberExpression(\n t.identifier('__injections'),\n t.identifier('hasOwnProperty'),\n false,\n ),\n [\n t.stringLiteral(dependencyString),\n ],\n ),\n t.memberExpression(\n t.identifier('__injections'),\n t.stringLiteral(dependencyString),\n true,\n ),\n path.node,\n ),\n ),\n );\n\n return dependencyString;\n}\n\nexport default function injectify(context, source, inputSourceMap) {\n const { ast } = transform(source, {\n babelrc: false,\n code: false,\n compact: false,\n filename: context.resourcePath,\n });\n\n const dependencies = [];\n traverse(ast, {\n CallExpression(path) {\n if (t.isIdentifier(path.node.callee, { name: 'require' })) {\n dependencies.push(processRequireCall(path));\n path.skip();\n }\n },\n });\n\n if (dependencies.length === 0) {\n context.emitWarning('The module you are trying to inject into doesn\\'t have any dependencies. ' +\n 'Are you sure you want to do this?');\n }\n\n const dependenciesArrayAst = t.arrayExpression(\n dependencies.map(dependency => t.stringLiteral(dependency)),\n );\n const wrapperModuleAst = t.file(t.program([\n wrapperTemplate({ SOURCE: ast, DEPENDENCIES: dependenciesArrayAst }),\n ]));\n\n return transformFromAst(wrapperModuleAst, source, {\n sourceMaps: context.sourceMap,\n sourceFileName: context.resourcePath,\n inputSourceMap,\n babelrc: false,\n compact: false,\n filename: context.resourcePath,\n });\n}\n\n\n\n// WEBPACK FOOTER //\n// src/injectify.js","// @flow\n\nimport { template } from 'babel-core';\n\nexport default template(`\n module.exports = function __injector(__injections) {\n __injections = __injections || {};\n\n (function __validateInjection() {\n var validDependencies = DEPENDENCIES;\n var injectedDependencies = Object.keys(__injections);\n var invalidInjectedDependencies = injectedDependencies.filter(function (dependency) {\n return validDependencies.indexOf(dependency) === -1;\n });\n\n if (invalidInjectedDependencies.length > 0) {\n var validDependenciesString = '- ' + validDependencies.join('\\\\n- ');\n var injectedDependenciesString = '- ' + injectedDependencies.join('\\\\n- ');\n var invalidDependenciesString = '- ' + invalidInjectedDependencies.join('\\\\n- ');\n\n throw new Error('Some of the injections you passed in are invalid.\\\\n' +\n 'Valid injection targets for this module are:\\\\n' + validDependenciesString + '\\\\n' +\n 'The following injections were passed in:\\\\n' + injectedDependenciesString + '\\\\n' +\n 'The following injections are invalid:\\\\n' + invalidDependenciesString + '\\\\n'\n );\n }\n })();\n\n var module = { exports: {} };\n var exports = module.exports;\n\n (function () {\n SOURCE\n })();\n\n return module.exports;\n }\n`);\n\n\n\n// WEBPACK FOOTER //\n// src/wrapper_template.js"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC7DA;;;;;;;;;;;;;;;;;;;ACIA;AACA;AAHA;AACA;;;;;AACA;AACA;AACA;AACA;AACA;AAJA;AAAA;AAAA;AACA;AAKA;AACA;AACA;;;;;;;;;;;;;ACqBA;AACA;AAhCA;AACA;AAAA;AACA;;;;;AAJA;AACA;AAIA;AACA;AACA;AACA;AAsBA;AACA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAJA;AADA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AANA;AACA;AAQA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AANA;AAQA;;;;;;;;;;;;;;ACrEA;AACA;AACA;AACA;;;;A","sourceRoot":""}
|
|
@@ -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
|
-
let themeConfig = super.loadConfig(theme, path.join('
|
|
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,62 +22,101 @@ 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
|
-
* @param {string} filePath Path to the file inside bundle directory where to find the configs
|
|
66
|
-
* @return {Object} Merged Configs loaded from all the bundles
|
|
90
|
+
* @param {string|string[]} filePath Path (or paths with fallback) to the file inside bundle directory where to find the configs
|
|
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 = {};
|
|
95
|
+
const filePaths = [].concat(filePath);
|
|
96
|
+
|
|
97
|
+
this._processedFiles = [];
|
|
70
98
|
this._bundles.forEach(bundle => {
|
|
71
|
-
|
|
72
|
-
|
|
99
|
+
let absolutePath;
|
|
100
|
+
|
|
101
|
+
for (const file of filePaths) {
|
|
102
|
+
absolutePath = path.resolve(bundle, file);
|
|
103
|
+
if (fs.existsSync(absolutePath)) {
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
absolutePath = void 0;
|
|
107
|
+
}
|
|
73
108
|
|
|
74
|
-
|
|
75
|
-
|
|
109
|
+
if (absolutePath) {
|
|
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);
|
|
116
|
+
configs = merge(configs, doc);
|
|
117
|
+
}
|
|
76
118
|
});
|
|
119
|
+
|
|
77
120
|
return configs;
|
|
78
121
|
}
|
|
79
122
|
}
|