@ui5/builder 3.0.4 → 3.0.6

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 CHANGED
@@ -2,7 +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.4...HEAD).
5
+ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-builder/compare/v3.0.6...HEAD).
6
+
7
+ <a name="v3.0.6"></a>
8
+ ## [v3.0.6] - 2023-06-21
9
+
10
+ <a name="v3.0.5"></a>
11
+ ## [v3.0.5] - 2023-06-05
12
+ ### Bug Fixes
13
+ - **bundle/Builder:** Remove sourceMappingURL from modules embedded as string [`a2f410c`](https://github.com/SAP/ui5-builder/commit/a2f410c32945a6a25fdf47b7b06ccb7f21ef5716)
14
+
15
+ ### Dependency Updates
16
+ - Bump xml2js from 0.5.0 to 0.6.0 [`9679830`](https://github.com/SAP/ui5-builder/commit/967983000d64324089740c5ffc0460dcca23866f)
17
+
6
18
 
7
19
  <a name="v3.0.4"></a>
8
20
  ## [v3.0.4] - 2023-05-23
@@ -791,6 +803,8 @@ to load the custom bundle file instead.
791
803
 
792
804
  ### Features
793
805
  - Add ability to configure component preloads and custom bundles [`2241e5f`](https://github.com/SAP/ui5-builder/commit/2241e5ff98fd95f1f80cc74959655ae7a9c660e7)
806
+ [v3.0.6]: https://github.com/SAP/ui5-builder/compare/v3.0.5...v3.0.6
807
+ [v3.0.5]: https://github.com/SAP/ui5-builder/compare/v3.0.4...v3.0.5
794
808
  [v3.0.4]: https://github.com/SAP/ui5-builder/compare/v3.0.3...v3.0.4
795
809
  [v3.0.3]: https://github.com/SAP/ui5-builder/compare/v3.0.2...v3.0.3
796
810
  [v3.0.2]: https://github.com/SAP/ui5-builder/compare/v3.0.1...v3.0.2
@@ -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 == Syntax.CallExpression &&
24
- node.callee.type === Syntax.MemberExpression &&
25
- isMethodCall(node.callee.object, CALL__SAP_UI_GETCORE) &&
26
- isIdentifier(node.callee.property, "initLibrary") &&
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
- const key = getPropertyKey(prop);
36
- const value = prop.value;
37
-
38
- if ( key === "noLibraryCSS" &&
39
- (value.type === Syntax.Literal && typeof value.value === "boolean") ) {
40
- libInfo.noLibraryCSS = value.value;
41
- } else if ( key === "types" && value.type == Syntax.ArrayExpression ) {
42
- libInfo.types = getStringArray(value, true);
43
- } else if ( key === "interfaces" && value.type == Syntax.ArrayExpression ) {
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
- return true; // abort, we're done
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
- const code = await resource.getBuffer();
80
- visit( parseJS(code) );
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
  }
@@ -439,7 +439,20 @@ class BundleBuilder {
439
439
  } else if ( /\.js$/.test(moduleName) /* implicitly: && info != null && info.requiresTopLevelScope */ ) {
440
440
  log.warn(
441
441
  `Module ${moduleName} requires top level scope and can only be embedded as a string (requires 'eval')`);
442
- outW.write( makeStringLiteral( (await resource.buffer()).toString() ) );
442
+ let moduleContent = (await resource.buffer()).toString();
443
+ if (this.options.sourceMap) {
444
+ // We are actually not interested in the source map this module might contain,
445
+ // but we should make sure to remove any "sourceMappingURL" from the module content before
446
+ // writing it to the bundle. Otherwise browser dev-tools might create unnecessary (and likely incorrect)
447
+ // requests for any referenced .map files
448
+ ({moduleContent} =
449
+ await this.getSourceMapForModule({
450
+ moduleName,
451
+ moduleContent,
452
+ resourcePath: resource.getPath()
453
+ }));
454
+ }
455
+ outW.write( makeStringLiteral(moduleContent) );
443
456
  } else if ( /\.html$/.test(moduleName) ) {
444
457
  const fileContent = (await resource.buffer()).toString();
445
458
  outW.write( makeStringLiteral( fileContent ) );
@@ -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.4",
3
+ "version": "3.0.6",
4
4
  "description": "UI5 Tooling - Builder",
5
5
  "author": {
6
6
  "name": "SAP SE",
@@ -129,30 +129,30 @@
129
129
  "less-openui5": "^0.11.6",
130
130
  "pretty-data": "^0.40.0",
131
131
  "rimraf": "^5.0.1",
132
- "semver": "^7.5.1",
133
- "terser": "^5.17.4",
132
+ "semver": "^7.5.2",
133
+ "terser": "^5.18.1",
134
134
  "workerpool": "^6.4.0",
135
- "xml2js": "^0.5.0"
135
+ "xml2js": "^0.6.0"
136
136
  },
137
137
  "devDependencies": {
138
138
  "@istanbuljs/esm-loader-hook": "^0.2.0",
139
- "@ui5/project": "^3.3.0",
140
- "ava": "^5.2.0",
139
+ "@ui5/project": "^3.3.2",
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.41.0",
147
+ "eslint": "^8.43.0",
148
148
  "eslint-config-google": "^0.14.0",
149
149
  "eslint-plugin-ava": "^14.0.0",
150
- "eslint-plugin-jsdoc": "^44.2.4",
151
- "esmock": "^2.2.3",
150
+ "eslint-plugin-jsdoc": "^46.2.6",
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.1.0",
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;