@ui5/builder 3.0.5 → 3.0.7
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/CHANGELOG.md +13 -2
- package/lib/lbt/analyzer/JSModuleAnalyzer.js +0 -12
- package/lib/lbt/analyzer/XMLTemplateAnalyzer.js +0 -4
- package/lib/lbt/analyzer/analyzeLibraryJS.js +108 -38
- package/lib/lbt/bundle/Resolver.js +50 -11
- package/lib/lbt/resources/ModuleInfo.js +1 -17
- package/lib/lbt/resources/ResourceCollector.js +2 -2
- package/lib/lbt/resources/ResourcePool.js +1 -3
- package/package.json +10 -10
- package/lib/lbt/analyzer/FioriElementsAnalyzer.js +0 -201
package/CHANGELOG.md
CHANGED
|
@@ -2,10 +2,19 @@
|
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
4
4
|
|
|
5
|
-
A list of unreleased changes can be found [here](https://github.com/SAP/ui5-builder/compare/v3.0.
|
|
5
|
+
A list of unreleased changes can be found [here](https://github.com/SAP/ui5-builder/compare/v3.0.7...HEAD).
|
|
6
|
+
|
|
7
|
+
<a name="v3.0.7"></a>
|
|
8
|
+
## [v3.0.7] - 2023-07-12
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
- Don't report a missing module that's contained in another bundle ([#915](https://github.com/SAP/ui5-builder/issues/915)) [`8f23f38`](https://github.com/SAP/ui5-builder/commit/8f23f388f6d64f313ae8a89d0fcaf39ba905a70b)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
<a name="v3.0.6"></a>
|
|
14
|
+
## [v3.0.6] - 2023-06-21
|
|
6
15
|
|
|
7
16
|
<a name="v3.0.5"></a>
|
|
8
|
-
## [v3.0.5] - 2023-06-
|
|
17
|
+
## [v3.0.5] - 2023-06-05
|
|
9
18
|
### Bug Fixes
|
|
10
19
|
- **bundle/Builder:** Remove sourceMappingURL from modules embedded as string [`a2f410c`](https://github.com/SAP/ui5-builder/commit/a2f410c32945a6a25fdf47b7b06ccb7f21ef5716)
|
|
11
20
|
|
|
@@ -800,6 +809,8 @@ to load the custom bundle file instead.
|
|
|
800
809
|
|
|
801
810
|
### Features
|
|
802
811
|
- Add ability to configure component preloads and custom bundles [`2241e5f`](https://github.com/SAP/ui5-builder/commit/2241e5ff98fd95f1f80cc74959655ae7a9c660e7)
|
|
812
|
+
[v3.0.7]: https://github.com/SAP/ui5-builder/compare/v3.0.6...v3.0.7
|
|
813
|
+
[v3.0.6]: https://github.com/SAP/ui5-builder/compare/v3.0.5...v3.0.6
|
|
803
814
|
[v3.0.5]: https://github.com/SAP/ui5-builder/compare/v3.0.4...v3.0.5
|
|
804
815
|
[v3.0.4]: https://github.com/SAP/ui5-builder/compare/v3.0.3...v3.0.4
|
|
805
816
|
[v3.0.3]: https://github.com/SAP/ui5-builder/compare/v3.0.2...v3.0.3
|
|
@@ -3,7 +3,6 @@ import escope from "escope";
|
|
|
3
3
|
import {fromUI5LegacyName, fromRequireJSName, resolveRelativeRequireJSName} from "../utils/ModuleName.js";
|
|
4
4
|
import moduleInfo from "../resources/ModuleInfo.js";
|
|
5
5
|
const ModuleFormat = moduleInfo.Format;
|
|
6
|
-
import {MODULE__JQUERY_SAP_GLOBAL, MODULE__UI5LOADER_AUTOCONFIG} from "../UI5ClientConstants.js";
|
|
7
6
|
import {
|
|
8
7
|
findOwnProperty,
|
|
9
8
|
getLocation,
|
|
@@ -335,17 +334,6 @@ class JSModuleAnalyzer {
|
|
|
335
334
|
}
|
|
336
335
|
}
|
|
337
336
|
|
|
338
|
-
// depending on the used module APIs, add an implicit dependency to the loader entry module
|
|
339
|
-
if ( info.format === ModuleFormat.UI5_LEGACY ) {
|
|
340
|
-
info.addImplicitDependency(MODULE__JQUERY_SAP_GLOBAL);
|
|
341
|
-
} else if ( info.format === ModuleFormat.UI5_DEFINE ) {
|
|
342
|
-
// Note: the implicit dependency for sap.ui.define modules points to the standard UI5
|
|
343
|
-
// loader config module. A more general approach would be to add a dependency to the loader
|
|
344
|
-
// only, but then standard configuration would be missed by dependency resolution
|
|
345
|
-
// (to be clarified)
|
|
346
|
-
info.addImplicitDependency(MODULE__UI5LOADER_AUTOCONFIG);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
337
|
if ( !bIsUi5Module ) {
|
|
350
338
|
// when there are no indicators for module APIs, mark the module as 'raw' module
|
|
351
339
|
info.rawModule = true;
|
|
@@ -178,8 +178,6 @@ class XMLTemplateAnalyzer {
|
|
|
178
178
|
// console.log(result);
|
|
179
179
|
// clear();
|
|
180
180
|
if ( isFragment ) {
|
|
181
|
-
// all fragments implicitly depend on the fragment class
|
|
182
|
-
this.info.addImplicitDependency(FRAGMENT_MODULE);
|
|
183
181
|
this._analyzeNode(result);
|
|
184
182
|
} else {
|
|
185
183
|
// views require a special handling of the root node
|
|
@@ -196,8 +194,6 @@ class XMLTemplateAnalyzer {
|
|
|
196
194
|
}
|
|
197
195
|
|
|
198
196
|
_analyzeViewRootNode(node) {
|
|
199
|
-
this.info.addImplicitDependency(XMLVIEW_MODULE);
|
|
200
|
-
|
|
201
197
|
const controllerName = getAttribute(node, XMLVIEW_CONTROLLERNAME_ATTRIBUTE);
|
|
202
198
|
if ( controllerName ) {
|
|
203
199
|
this._addDependency( fromUI5LegacyName(controllerName, ".controller.js"), this.conditional );
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
|
|
2
2
|
import {parseJS, Syntax, VisitorKeys} from "../utils/parseUtils.js";
|
|
3
3
|
import {getPropertyKey, isMethodCall, isIdentifier, getStringArray} from "../utils/ASTUtils.js";
|
|
4
|
+
import SapUiDefineCall from "../calls/SapUiDefine.js";
|
|
4
5
|
import {getLogger} from "@ui5/logger";
|
|
5
6
|
const log = getLogger("lbt:analyzer:LibraryJS");
|
|
6
7
|
|
|
7
8
|
const CALL__SAP_UI_GETCORE = ["sap", "ui", "getCore"];
|
|
9
|
+
const CALL_SAP_UI_DEFINE = ["sap", "ui", "define"];
|
|
10
|
+
const CALL_AMD_DEFINE = ["define"];
|
|
11
|
+
const AMD__LIB_INIT_RESOURCES = [
|
|
12
|
+
// Dependency definition, init library method name
|
|
13
|
+
["sap/ui/core/Core.js", "initLibrary"],
|
|
14
|
+
["sap/ui/core/Lib.js", "init"]
|
|
15
|
+
];
|
|
8
16
|
|
|
9
17
|
/*
|
|
10
18
|
* Static Code Analyzer that extracts library information from the sap.ui.getCore().initLibrary()
|
|
@@ -19,45 +27,51 @@ async function analyze(resource) {
|
|
|
19
27
|
interfaces: []
|
|
20
28
|
};
|
|
21
29
|
|
|
30
|
+
const code = await resource.getBuffer();
|
|
31
|
+
const rootNode = parseJS(code);
|
|
32
|
+
let analyzedDefineCall = null;
|
|
33
|
+
visit( rootNode );
|
|
34
|
+
|
|
22
35
|
function visit(node) {
|
|
23
|
-
if ( node.type
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
node.arguments.length === 1 &&
|
|
28
|
-
node.arguments[0].type === Syntax.ObjectExpression ) {
|
|
29
|
-
node.arguments[0].properties.forEach( (prop) => {
|
|
30
|
-
if (prop.type === Syntax.SpreadElement) {
|
|
31
|
-
// SpreadElements are currently not supported
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
36
|
+
if ( node.type === Syntax.CallExpression ) {
|
|
37
|
+
// Define call would always be at the top of the tree, so it'd
|
|
38
|
+
// be the first significant thing that's been found.
|
|
39
|
+
analyzedDefineCall = analyzedDefineCall || analyzeSapUiDefineCalls(node);
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
libInfo.interfaces = getStringArray(value, true);
|
|
45
|
-
} else if ( key === "controls" && value.type == Syntax.ArrayExpression ) {
|
|
46
|
-
libInfo.controls = getStringArray(value, true);
|
|
47
|
-
} else if ( key === "elements" && value.type == Syntax.ArrayExpression ) {
|
|
48
|
-
libInfo.elements = getStringArray(value, true);
|
|
49
|
-
} else if ( ["designtime", "dependencies", "extensions", "name", "version"].includes(key) ) {
|
|
50
|
-
// do nothing, for all other supported properties
|
|
51
|
-
} else {
|
|
52
|
-
log.error(
|
|
53
|
-
"Unexpected property '" + key +
|
|
54
|
-
"' or wrong type for '" + key +
|
|
55
|
-
"' in sap.ui.getCore().initLibrary call in '" + resource.getPath() + "'"
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
});
|
|
41
|
+
if (isInitLibraryCall(node)) {
|
|
42
|
+
node.arguments[0].properties.forEach( (prop) => {
|
|
43
|
+
if (prop.type === Syntax.SpreadElement) {
|
|
44
|
+
// SpreadElements are currently not supported
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const key = getPropertyKey(prop);
|
|
49
|
+
const value = prop.value;
|
|
59
50
|
|
|
60
|
-
|
|
51
|
+
if ( key === "noLibraryCSS" &&
|
|
52
|
+
(value.type === Syntax.Literal && typeof value.value === "boolean") ) {
|
|
53
|
+
libInfo.noLibraryCSS = value.value;
|
|
54
|
+
} else if ( key === "types" && value.type == Syntax.ArrayExpression ) {
|
|
55
|
+
libInfo.types = getStringArray(value, true);
|
|
56
|
+
} else if ( key === "interfaces" && value.type == Syntax.ArrayExpression ) {
|
|
57
|
+
libInfo.interfaces = getStringArray(value, true);
|
|
58
|
+
} else if ( key === "controls" && value.type == Syntax.ArrayExpression ) {
|
|
59
|
+
libInfo.controls = getStringArray(value, true);
|
|
60
|
+
} else if ( key === "elements" && value.type == Syntax.ArrayExpression ) {
|
|
61
|
+
libInfo.elements = getStringArray(value, true);
|
|
62
|
+
} else if ( ["designtime", "dependencies", "extensions", "name", "version"].includes(key) ) {
|
|
63
|
+
// do nothing, for all other supported properties
|
|
64
|
+
} else {
|
|
65
|
+
log.error(
|
|
66
|
+
"Unexpected property '" + key +
|
|
67
|
+
"' or wrong type for '" + key +
|
|
68
|
+
"' for a library initialization call in '" + resource.getPath() + "'"
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return true; // abort, we're done
|
|
74
|
+
}
|
|
61
75
|
}
|
|
62
76
|
|
|
63
77
|
for ( const key of VisitorKeys[node.type] ) {
|
|
@@ -76,8 +90,64 @@ async function analyze(resource) {
|
|
|
76
90
|
return false;
|
|
77
91
|
}
|
|
78
92
|
|
|
79
|
-
|
|
80
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Finds and extracts dependencies from sap.ui.define call
|
|
95
|
+
*
|
|
96
|
+
* @param {Node} node
|
|
97
|
+
* @returns {SapUiDefineCall}
|
|
98
|
+
*/
|
|
99
|
+
function analyzeSapUiDefineCalls(node) {
|
|
100
|
+
// Analyze sap.ui.define calls
|
|
101
|
+
if ( [CALL_AMD_DEFINE, CALL_SAP_UI_DEFINE].some(
|
|
102
|
+
(defCall) => isMethodCall(node, defCall)) ) {
|
|
103
|
+
const defCall = new SapUiDefineCall(node, resource.getName());
|
|
104
|
+
|
|
105
|
+
return AMD__LIB_INIT_RESOURCES
|
|
106
|
+
// Resolve dependency variable names.
|
|
107
|
+
.map((dependency) => [defCall.findImportName(dependency[0]), dependency[1]])
|
|
108
|
+
// Remove potential dependencies that are not actually present.
|
|
109
|
+
.filter((dependency) => dependency[0]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Determines whether the node is an initLibrary call.
|
|
117
|
+
*
|
|
118
|
+
* @param {Node} node
|
|
119
|
+
* @returns {boolean}
|
|
120
|
+
*/
|
|
121
|
+
function isInitLibraryCall(node) {
|
|
122
|
+
// sap.ui.getCore()
|
|
123
|
+
if (
|
|
124
|
+
node.type == Syntax.CallExpression &&
|
|
125
|
+
node.callee.type === Syntax.MemberExpression &&
|
|
126
|
+
isMethodCall(node.callee.object, CALL__SAP_UI_GETCORE) &&
|
|
127
|
+
isIdentifier(node.callee.property, "initLibrary") &&
|
|
128
|
+
node.arguments.length === 1 &&
|
|
129
|
+
node.arguments[0].type === Syntax.ObjectExpression
|
|
130
|
+
) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Find a CallExpression that is initLibrary
|
|
135
|
+
// i.e. node.callee.object === "Core" && node.callee.property === "initLibrary"
|
|
136
|
+
if (
|
|
137
|
+
node.type === Syntax.CallExpression &&
|
|
138
|
+
node.callee.type === Syntax.MemberExpression &&
|
|
139
|
+
analyzedDefineCall &&
|
|
140
|
+
analyzedDefineCall.some((potentialDependency) =>
|
|
141
|
+
isMethodCall(node, potentialDependency)
|
|
142
|
+
) &&
|
|
143
|
+
node.arguments.length === 1 &&
|
|
144
|
+
node.arguments[0].type === Syntax.ObjectExpression
|
|
145
|
+
) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
81
151
|
|
|
82
152
|
return libInfo;
|
|
83
153
|
}
|
|
@@ -51,6 +51,16 @@ class BundleResolver {
|
|
|
51
51
|
let selectedResources = Object.create(null);
|
|
52
52
|
let selectedResourcesSequence = [];
|
|
53
53
|
const pool = this.pool;
|
|
54
|
+
/**
|
|
55
|
+
* Names of modules that are required in some way but could not be found
|
|
56
|
+
* in the resource pool.
|
|
57
|
+
*/
|
|
58
|
+
const missingModules = Object.create(null);
|
|
59
|
+
/**
|
|
60
|
+
* Names of modules that are included in non-decomposable bundles.
|
|
61
|
+
* If they occur in the missingModules, then this is not an error.
|
|
62
|
+
*/
|
|
63
|
+
const includedModules = new Set();
|
|
54
64
|
|
|
55
65
|
/**
|
|
56
66
|
* @param {JSModuleSectionDefinition} section
|
|
@@ -71,11 +81,16 @@ class BundleResolver {
|
|
|
71
81
|
}
|
|
72
82
|
|
|
73
83
|
function checkForDecomposableBundle(resource) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
const isBundle =
|
|
85
|
+
resource?.info?.subModules.length > 0 &&
|
|
86
|
+
!/(?:^|\/)library.js$/.test(resource.info.name);
|
|
87
|
+
|
|
88
|
+
if (!isBundle) {
|
|
89
|
+
return {
|
|
90
|
+
resource,
|
|
91
|
+
isBundle,
|
|
92
|
+
decomposable: false
|
|
93
|
+
};
|
|
79
94
|
}
|
|
80
95
|
|
|
81
96
|
return Promise.all(
|
|
@@ -83,7 +98,11 @@ class BundleResolver {
|
|
|
83
98
|
).then((modules) => {
|
|
84
99
|
// it might look more natural to expect 'all' embedded modules to exist in the pool,
|
|
85
100
|
// but expecting only 'some' module to exist is a more conservative approach
|
|
86
|
-
return
|
|
101
|
+
return {
|
|
102
|
+
resource,
|
|
103
|
+
isBundle,
|
|
104
|
+
decomposable: modules.some(($) => ($))
|
|
105
|
+
};
|
|
87
106
|
});
|
|
88
107
|
}
|
|
89
108
|
|
|
@@ -105,20 +124,29 @@ class BundleResolver {
|
|
|
105
124
|
.catch( (err) => {
|
|
106
125
|
// if the caller provided an error message, log it
|
|
107
126
|
if ( msg ) {
|
|
108
|
-
|
|
127
|
+
missingModules[resourceName] ??= [];
|
|
128
|
+
missingModules[resourceName].push(msg);
|
|
109
129
|
}
|
|
110
130
|
// return undefined
|
|
111
131
|
})
|
|
112
132
|
.then( (resource) => checkForDecomposableBundle(resource) )
|
|
113
|
-
.then( ({resource, decomposable}) => {
|
|
133
|
+
.then( ({resource, isBundle, decomposable}) => {
|
|
114
134
|
const dependencyInfo = resource && resource.info;
|
|
115
135
|
let promises = [];
|
|
116
136
|
|
|
137
|
+
if ( isBundle && !decomposable ) {
|
|
138
|
+
resource.info.subModules.forEach(
|
|
139
|
+
(included) => {
|
|
140
|
+
includedModules.add(included);
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
117
145
|
if ( decomposable ) {
|
|
118
146
|
// bundles are not added, only their embedded modules
|
|
119
147
|
promises = dependencyInfo.subModules.map( (included) => {
|
|
120
148
|
return checkAndAddResource(included, depth + 1,
|
|
121
|
-
|
|
149
|
+
`**** error: missing submodule ${included}, included by ${resourceName}`);
|
|
122
150
|
});
|
|
123
151
|
} else if ( resource != null ) {
|
|
124
152
|
// trace.trace(" checking dependencies of " + resource.name );
|
|
@@ -136,8 +164,8 @@ class BundleResolver {
|
|
|
136
164
|
return;
|
|
137
165
|
}
|
|
138
166
|
|
|
139
|
-
return checkAndAddResource(
|
|
140
|
-
|
|
167
|
+
return checkAndAddResource(required, depth + 1,
|
|
168
|
+
`**** error: missing module ${required}, required by ${resourceName}`);
|
|
141
169
|
});
|
|
142
170
|
}
|
|
143
171
|
|
|
@@ -317,6 +345,17 @@ class BundleResolver {
|
|
|
317
345
|
// NODE-TODO if ( PerfMeasurement.ACTIVE ) PerfMeasurement.stop(PerfKeys.RESOLVE_MODULE);
|
|
318
346
|
|
|
319
347
|
return previous.then( function() {
|
|
348
|
+
// ignore missing modules that have been found in non-decomposable bundles
|
|
349
|
+
includedModules.forEach((included) => delete missingModules[included]);
|
|
350
|
+
|
|
351
|
+
// report the remaining missing modules
|
|
352
|
+
Object.keys(missingModules).sort().forEach((missing) => {
|
|
353
|
+
const messages = missingModules[missing];
|
|
354
|
+
messages.sort().forEach((msg) => {
|
|
355
|
+
log.error(msg);
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
320
359
|
log.verbose(" Resolving bundle done");
|
|
321
360
|
|
|
322
361
|
return resolved;
|
|
@@ -6,14 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
const STRICT = 0;
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* An implicit dependency is also strict, but has not been declared. E.g. each UI5 module depends on
|
|
11
|
-
* jquery.sap.global.
|
|
12
|
-
*
|
|
13
|
-
* @private
|
|
14
|
-
*/
|
|
15
|
-
const IMPLICIT = 1;
|
|
16
|
-
|
|
17
9
|
/**
|
|
18
10
|
* A conditional dependency has to be resolved only under certain conditions that typically are
|
|
19
11
|
* checked at runtime.
|
|
@@ -112,7 +104,7 @@ class ModuleInfo {
|
|
|
112
104
|
// included already as a submodule.
|
|
113
105
|
// If the dependency was known already, update the kind
|
|
114
106
|
// only when the new kind is stronger than the current one.
|
|
115
|
-
// STRICT is stronger than
|
|
107
|
+
// STRICT is stronger than CONDITIONAL
|
|
116
108
|
if ( dependency &&
|
|
117
109
|
dependency !== this.name &&
|
|
118
110
|
this.subModules.indexOf(dependency) < 0 &&
|
|
@@ -121,10 +113,6 @@ class ModuleInfo {
|
|
|
121
113
|
}
|
|
122
114
|
}
|
|
123
115
|
|
|
124
|
-
addImplicitDependency(dependency) {
|
|
125
|
-
this._addDependency(dependency, IMPLICIT);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
116
|
addDependency(dependency, conditional) {
|
|
129
117
|
this._addDependency(dependency, conditional ? CONDITIONAL : STRICT);
|
|
130
118
|
}
|
|
@@ -175,10 +163,6 @@ class ModuleInfo {
|
|
|
175
163
|
return this._dependencies[dependency] === CONDITIONAL;
|
|
176
164
|
}
|
|
177
165
|
|
|
178
|
-
isImplicitDependency(dependency) {
|
|
179
|
-
return this._dependencies[dependency] === IMPLICIT;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
166
|
get name() {
|
|
183
167
|
return this._name;
|
|
184
168
|
}
|
|
@@ -124,7 +124,7 @@ class ResourceCollector {
|
|
|
124
124
|
moduleInfo.dependencies.forEach((dep) => {
|
|
125
125
|
if ( moduleInfo.isConditionalDependency(dep) ) {
|
|
126
126
|
resourceInfo.condRequired.add(dep);
|
|
127
|
-
} else
|
|
127
|
+
} else {
|
|
128
128
|
resourceInfo.required.add(dep);
|
|
129
129
|
}
|
|
130
130
|
});
|
|
@@ -158,7 +158,7 @@ class ResourceCollector {
|
|
|
158
158
|
if (!resourceInfo.required.has(dep)) {
|
|
159
159
|
resourceInfo.condRequired.add(dep);
|
|
160
160
|
}
|
|
161
|
-
} else
|
|
161
|
+
} else {
|
|
162
162
|
// Move module from condRequired to required
|
|
163
163
|
if (resourceInfo.condRequired.has(dep)) {
|
|
164
164
|
resourceInfo.condRequired.delete(dep);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {parseJS} from "../utils/parseUtils.js";
|
|
2
2
|
import ComponentAnalyzer from "../analyzer/ComponentAnalyzer.js";
|
|
3
3
|
import SmartTemplateAnalyzer from "../analyzer/SmartTemplateAnalyzer.js";
|
|
4
|
-
import FioriElementsAnalyzer from "../analyzer/FioriElementsAnalyzer.js";
|
|
5
4
|
import XMLCompositeAnalyzer from "../analyzer/XMLCompositeAnalyzer.js";
|
|
6
5
|
import JSModuleAnalyzer from "../analyzer/JSModuleAnalyzer.js";
|
|
7
6
|
import XMLTemplateAnalyzer from "../analyzer/XMLTemplateAnalyzer.js";
|
|
@@ -94,8 +93,7 @@ async function determineDependencyInfo(resource, rawInfo, pool) {
|
|
|
94
93
|
if ( /(?:^|\/)Component\.js/.test(resource.name) ) {
|
|
95
94
|
promises.push(
|
|
96
95
|
new ComponentAnalyzer(pool).analyze(resource, info),
|
|
97
|
-
new SmartTemplateAnalyzer(pool).analyze(resource, info)
|
|
98
|
-
new FioriElementsAnalyzer(pool).analyze(resource, info)
|
|
96
|
+
new SmartTemplateAnalyzer(pool).analyze(resource, info)
|
|
99
97
|
);
|
|
100
98
|
}
|
|
101
99
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ui5/builder",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.7",
|
|
4
4
|
"description": "UI5 Tooling - Builder",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "SAP SE",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"unit": "rimraf test/tmp && ava",
|
|
39
39
|
"unit-verbose": "rimraf test/tmp && cross-env UI5_LOG_LVL=verbose ava --verbose --serial",
|
|
40
40
|
"unit-watch": "rimraf test/tmp && ava --watch",
|
|
41
|
-
"unit-xunit": "rimraf test/tmp && ava --node-arguments=\"--experimental-loader=@istanbuljs/esm-loader-hook\" --tap --timeout=1m | tap-xunit --dontUseCommentsAsTestNames=true > test-results.xml",
|
|
41
|
+
"unit-xunit": "rimraf test/tmp && ava --no-worker-threads --node-arguments=\"--experimental-loader=@istanbuljs/esm-loader-hook\" --tap --timeout=1m | tap-xunit --dontUseCommentsAsTestNames=true > test-results.xml",
|
|
42
42
|
"unit-inspect": "cross-env UI5_LOG_LVL=verbose ava debug --break",
|
|
43
43
|
"coverage": "rimraf test/tmp && nyc ava --node-arguments=\"--experimental-loader=@istanbuljs/esm-loader-hook\"",
|
|
44
44
|
"coverage-xunit": "nyc --reporter=text --reporter=text-summary --reporter=cobertura npm run unit-xunit",
|
|
@@ -123,36 +123,36 @@
|
|
|
123
123
|
"cheerio": "1.0.0-rc.12",
|
|
124
124
|
"escape-unicode": "^0.2.0",
|
|
125
125
|
"escope": "^4.0.0",
|
|
126
|
-
"espree": "^9.
|
|
126
|
+
"espree": "^9.6.0",
|
|
127
127
|
"graceful-fs": "^4.2.11",
|
|
128
128
|
"jsdoc": "^4.0.2",
|
|
129
129
|
"less-openui5": "^0.11.6",
|
|
130
130
|
"pretty-data": "^0.40.0",
|
|
131
131
|
"rimraf": "^5.0.1",
|
|
132
|
-
"semver": "^7.5.
|
|
133
|
-
"terser": "^5.
|
|
132
|
+
"semver": "^7.5.4",
|
|
133
|
+
"terser": "^5.18.2",
|
|
134
134
|
"workerpool": "^6.4.0",
|
|
135
135
|
"xml2js": "^0.6.0"
|
|
136
136
|
},
|
|
137
137
|
"devDependencies": {
|
|
138
138
|
"@istanbuljs/esm-loader-hook": "^0.2.0",
|
|
139
|
-
"@ui5/project": "^3.
|
|
140
|
-
"ava": "^5.3.
|
|
139
|
+
"@ui5/project": "^3.4.1",
|
|
140
|
+
"ava": "^5.3.1",
|
|
141
141
|
"chai": "^4.3.7",
|
|
142
142
|
"chai-fs": "^2.0.0",
|
|
143
143
|
"chokidar-cli": "^3.0.0",
|
|
144
144
|
"cross-env": "^7.0.3",
|
|
145
145
|
"depcheck": "^1.4.3",
|
|
146
146
|
"docdash": "^2.0.1",
|
|
147
|
-
"eslint": "^8.
|
|
147
|
+
"eslint": "^8.44.0",
|
|
148
148
|
"eslint-config-google": "^0.14.0",
|
|
149
149
|
"eslint-plugin-ava": "^14.0.0",
|
|
150
|
-
"eslint-plugin-jsdoc": "^46.
|
|
150
|
+
"eslint-plugin-jsdoc": "^46.4.3",
|
|
151
151
|
"esmock": "^2.3.1",
|
|
152
152
|
"nyc": "^15.1.0",
|
|
153
153
|
"open-cli": "^7.2.0",
|
|
154
154
|
"recursive-readdir": "^2.2.3",
|
|
155
|
-
"sinon": "^15.
|
|
155
|
+
"sinon": "^15.2.0",
|
|
156
156
|
"tap-xunit": "^2.4.1"
|
|
157
157
|
}
|
|
158
158
|
}
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Analyzes a FioriElements app and its underlying template components to collect dependency information.
|
|
3
|
-
*
|
|
4
|
-
* Tries to find a manifest.json in the same package. If it is found and if
|
|
5
|
-
* it is a valid JSON, an "sap.fe" section is searched and evaluated in the following way
|
|
6
|
-
* - for each entity set in the "entitySets" object, each sub-entry is checked for a "default"."template" property
|
|
7
|
-
* - when found, that string is interpreted as the short name of a template component in package sap.fe.templates
|
|
8
|
-
* - a dependency to that template component is added to the analyzed app
|
|
9
|
-
*
|
|
10
|
-
* For a full analysis, "routing" also should be taken into account. Only when a sub-entry of the entity set
|
|
11
|
-
* is referenced by a route, then the template for that entry will be used. Routes thereby could form entry points.
|
|
12
|
-
*
|
|
13
|
-
* <pre>
|
|
14
|
-
* {
|
|
15
|
-
* ...
|
|
16
|
-
*
|
|
17
|
-
* "sap.fe" : {
|
|
18
|
-
* "entitySets" : {
|
|
19
|
-
* "C_AIVS_MDBU_ArtistTP" : {
|
|
20
|
-
* "feed": {
|
|
21
|
-
* "default": {
|
|
22
|
-
* "template": "ListReport"
|
|
23
|
-
* }
|
|
24
|
-
* },
|
|
25
|
-
* "entry" : {
|
|
26
|
-
* "default" : {
|
|
27
|
-
* "outbound" : "musicV2Display"
|
|
28
|
-
* }
|
|
29
|
-
* }
|
|
30
|
-
* }
|
|
31
|
-
* },
|
|
32
|
-
* "routing" : {
|
|
33
|
-
* "routes" :{
|
|
34
|
-
* "ArtistList": {
|
|
35
|
-
* "target": "C_AIVS_MDBU_ArtistTP/feed"
|
|
36
|
-
* }
|
|
37
|
-
* }
|
|
38
|
-
* }
|
|
39
|
-
* }
|
|
40
|
-
*
|
|
41
|
-
* ...
|
|
42
|
-
* </pre>
|
|
43
|
-
*
|
|
44
|
-
* The template component is analyzed in the following way:
|
|
45
|
-
* - precondition: template component class is defined in an AMD-style module, using define or sap.ui.define
|
|
46
|
-
* - precondition: the module 'sap/fe/core/TemplateAssembler' is imported
|
|
47
|
-
* - precondition: a call to TemplateAssembler.getTemplateComponent is used to define the component class
|
|
48
|
-
* - precondition: that call is used in a top level return statement of the factory function
|
|
49
|
-
* - precondition: necessary parameters to that call are given as an object literal (no further coding)
|
|
50
|
-
* - precondition: the settings define a managed property property 'metadata.properties.templateName' with a
|
|
51
|
-
* defaultValue of type string
|
|
52
|
-
* The default value of the property represents the template view of the template component.
|
|
53
|
-
* The manifest of the template app in theory could specify an alternative template as setting.templateName,
|
|
54
|
-
* but as of June 2017, this possibility is currently not used.
|
|
55
|
-
*
|
|
56
|
-
* This class can handle multiple concurrent analysis calls, it has no instance state other than the pool
|
|
57
|
-
* (which is readonly).
|
|
58
|
-
*/
|
|
59
|
-
|
|
60
|
-
import {fromUI5LegacyName} from "../utils/ModuleName.js";
|
|
61
|
-
import SapUiDefine from "../calls/SapUiDefine.js";
|
|
62
|
-
import {parseJS, Syntax} from "../utils/parseUtils.js";
|
|
63
|
-
import {getValue, isMethodCall, getStringValue} from "../utils/ASTUtils.js";
|
|
64
|
-
import {getLogger} from "@ui5/logger";
|
|
65
|
-
const log = getLogger("lbt:analyzer:FioriElementAnalyzer");
|
|
66
|
-
|
|
67
|
-
// ---------------------------------------------------------------------------------------------------------
|
|
68
|
-
|
|
69
|
-
function each(obj, fn) {
|
|
70
|
-
if ( obj ) {
|
|
71
|
-
Object.keys(obj).forEach(
|
|
72
|
-
(key) => fn(obj[key], key, obj)
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const CALL_DEFINE = ["define"];
|
|
78
|
-
const CALL_SAP_UI_DEFINE = ["sap", "ui", "define"];
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Analyzes the manifest for a Fiori Elements application (next to its Component.js) to find more dependencies.
|
|
82
|
-
*
|
|
83
|
-
* @private
|
|
84
|
-
*/
|
|
85
|
-
class FioriElementsAnalyzer {
|
|
86
|
-
constructor(pool) {
|
|
87
|
-
this._pool = pool;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async analyze(resource, info) {
|
|
91
|
-
// ignore base class for components
|
|
92
|
-
if ( resource.name === "sap/ui/core/Component.js" ) {
|
|
93
|
-
return info;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const manifestName = resource.name.replace(/Component\.js$/, "manifest.json");
|
|
97
|
-
try {
|
|
98
|
-
const manifestResource = await this._pool.findResource(manifestName).catch(() => null);
|
|
99
|
-
if ( manifestResource ) {
|
|
100
|
-
const fileContent = await manifestResource.buffer();
|
|
101
|
-
await this._analyzeManifest( JSON.parse(fileContent.toString()), info );
|
|
102
|
-
} else {
|
|
103
|
-
log.verbose(`No manifest found for '${resource.name}', skipping analysis`);
|
|
104
|
-
}
|
|
105
|
-
} catch (err) {
|
|
106
|
-
log.error(`An error occurred while analyzing template app ${resource.name} (ignored): ${err.message}`);
|
|
107
|
-
log.verbose(err.stack);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return info;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Evaluates a manifest after it has been read and parsed
|
|
115
|
-
* and adds any newly found dependencies to the given info object.
|
|
116
|
-
*
|
|
117
|
-
* @param {object} manifest JSON with app descriptor structure
|
|
118
|
-
* @param {ModuleInfo} info ModuleInfo object that should be enriched
|
|
119
|
-
* @returns {ModuleInfo} ModuleInfo object that should be enriched
|
|
120
|
-
* @private
|
|
121
|
-
*/
|
|
122
|
-
async _analyzeManifest( manifest, info ) {
|
|
123
|
-
const promises = [];
|
|
124
|
-
const st = (manifest && manifest["sap.fe"]) || {};
|
|
125
|
-
|
|
126
|
-
each(st.entitySets, (entitySetCfg) => {
|
|
127
|
-
each(entitySetCfg, (activityCfg, activity) => {
|
|
128
|
-
if ( activity === "entitySet" ) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
each(activityCfg, (actionCfg) => {
|
|
132
|
-
if ( actionCfg.template ) {
|
|
133
|
-
const module = fromUI5LegacyName( "sap.fe.templates." +
|
|
134
|
-
actionCfg.template + ".Component" );
|
|
135
|
-
log.verbose(`Template app: Add dependency to template component ${module}`);
|
|
136
|
-
info.addDependency(module);
|
|
137
|
-
promises.push( this._analyzeTemplateComponent(module, actionCfg, info) );
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
return Promise.all(promises);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async _analyzeTemplateComponent(moduleName, pageConfig, appInfo) {
|
|
147
|
-
// console.log("analyzing template component %s", moduleName);
|
|
148
|
-
const resource = await this._pool.findResource(moduleName);
|
|
149
|
-
const code = await resource.buffer();
|
|
150
|
-
const ast = parseJS(code);
|
|
151
|
-
const defaultTemplateName = this._analyzeAST(moduleName, ast);
|
|
152
|
-
const templateName = (pageConfig.component && pageConfig.component.settings &&
|
|
153
|
-
pageConfig.component.settings.templateName) || defaultTemplateName;
|
|
154
|
-
if ( templateName ) {
|
|
155
|
-
const templateModuleName = fromUI5LegacyName( templateName, ".view.xml" );
|
|
156
|
-
log.verbose(`Template app: Add dependency to template view ${templateModuleName}`);
|
|
157
|
-
appInfo.addDependency(templateModuleName);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
_analyzeAST(moduleName, ast) {
|
|
162
|
-
let templateName = "";
|
|
163
|
-
if ( ast.body.length > 0 && (isMethodCall(ast.body[0].expression, CALL_SAP_UI_DEFINE) ||
|
|
164
|
-
isMethodCall(ast.body[0].expression, CALL_DEFINE)) ) {
|
|
165
|
-
const defineCall = new SapUiDefine(ast.body[0].expression, moduleName);
|
|
166
|
-
const TA = defineCall.findImportName("sap/fe/core/TemplateAssembler.js");
|
|
167
|
-
// console.log("local name for TemplateAssembler: %s", TA);
|
|
168
|
-
if ( TA && defineCall.factory ) {
|
|
169
|
-
if (defineCall.factory.type === Syntax.ArrowFunctionExpression &&
|
|
170
|
-
defineCall.factory.expression === true) {
|
|
171
|
-
if ( this._isTemplateClassDefinition(TA, defineCall.factory.body) ) {
|
|
172
|
-
templateName =
|
|
173
|
-
this._analyzeTemplateClassDefinition(defineCall.factory.body.arguments[2]) || templateName;
|
|
174
|
-
}
|
|
175
|
-
} else {
|
|
176
|
-
defineCall.factory.body.body.forEach( (stmt) => {
|
|
177
|
-
if ( stmt.type === Syntax.ReturnStatement &&
|
|
178
|
-
this._isTemplateClassDefinition(TA, stmt.argument)
|
|
179
|
-
) {
|
|
180
|
-
templateName =
|
|
181
|
-
this._analyzeTemplateClassDefinition(stmt.argument.arguments[2]) || templateName;
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return templateName;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
_isTemplateClassDefinition(TA, node) {
|
|
191
|
-
return isMethodCall(node, [TA, "getTemplateComponent"]) &&
|
|
192
|
-
node.arguments.length > 2 &&
|
|
193
|
-
node.arguments[2].type === "ObjectExpression";
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
_analyzeTemplateClassDefinition(clazz) {
|
|
197
|
-
return getStringValue(getValue(clazz, ["metadata", "properties", "templateName", "defaultValue"]));
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export default FioriElementsAnalyzer;
|