@ui5/builder 3.0.0-alpha.0 → 3.0.0-alpha.3

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +58 -1
  2. package/index.js +4 -16
  3. package/jsdoc.json +0 -1
  4. package/lib/builder/BuildContext.js +17 -0
  5. package/lib/builder/ProjectBuildContext.js +9 -7
  6. package/lib/builder/builder.js +1 -2
  7. package/lib/lbt/analyzer/JSModuleAnalyzer.js +18 -3
  8. package/lib/lbt/analyzer/XMLTemplateAnalyzer.js +2 -1
  9. package/lib/lbt/bundle/AutoSplitter.js +10 -24
  10. package/lib/lbt/bundle/Builder.js +363 -134
  11. package/lib/lbt/bundle/BundleWriter.js +17 -0
  12. package/lib/lbt/resources/LocatorResource.js +6 -8
  13. package/lib/lbt/resources/LocatorResourcePool.js +8 -4
  14. package/lib/lbt/resources/Resource.js +7 -0
  15. package/lib/lbt/resources/ResourceCollector.js +33 -15
  16. package/lib/lbt/utils/parseUtils.js +1 -1
  17. package/lib/processors/bundlers/moduleBundler.js +40 -14
  18. package/lib/processors/minifier.js +90 -0
  19. package/lib/processors/resourceListCreator.js +2 -16
  20. package/lib/tasks/TaskUtil.js +9 -9
  21. package/lib/tasks/bundlers/generateBundle.js +81 -13
  22. package/lib/tasks/bundlers/generateComponentPreload.js +20 -6
  23. package/lib/tasks/bundlers/generateFlexChangesBundle.js +3 -2
  24. package/lib/tasks/bundlers/generateLibraryPreload.js +65 -10
  25. package/lib/tasks/bundlers/generateStandaloneAppBundle.js +46 -12
  26. package/lib/tasks/bundlers/utils/createModuleNameMapping.js +30 -0
  27. package/lib/tasks/generateResourcesJson.js +14 -8
  28. package/lib/tasks/minify.js +41 -0
  29. package/lib/tasks/taskRepository.js +6 -2
  30. package/lib/types/application/ApplicationBuilder.js +22 -31
  31. package/lib/types/library/LibraryBuilder.js +23 -29
  32. package/lib/types/themeLibrary/ThemeLibraryBuilder.js +1 -0
  33. package/package.json +16 -14
  34. package/lib/processors/debugFileCreator.js +0 -52
  35. package/lib/processors/resourceCopier.js +0 -24
  36. package/lib/processors/uglifier.js +0 -45
  37. package/lib/tasks/createDebugFiles.js +0 -30
  38. package/lib/tasks/uglify.js +0 -33
@@ -11,6 +11,8 @@ const SPACES_OR_TABS_ONLY = /^[ \t]+$/;
11
11
  *
12
12
  * Most methods have been extracted from JSMergeWriter.
13
13
  *
14
+ * columnOffset and lineOffset are used for sourcemap merging as reference to where we are at a given point in time
15
+ *
14
16
  * @author Frank Weigel
15
17
  * @since 1.27.0
16
18
  * @private
@@ -18,6 +20,8 @@ const SPACES_OR_TABS_ONLY = /^[ \t]+$/;
18
20
  class BundleWriter {
19
21
  constructor() {
20
22
  this.buf = "";
23
+ this.lineOffset = 0;
24
+ this.columnOffset = 0;
21
25
  this.segments = [];
22
26
  this.currentSegment = null;
23
27
  this.currentSourceIndex = 0;
@@ -28,6 +32,11 @@ class BundleWriter {
28
32
  let writeBuf = "";
29
33
  for ( let i = 0; i < str.length; i++ ) {
30
34
  writeBuf += str[i];
35
+ if (str[i] != null && str[i].split) {
36
+ const strSplit = str[i].split(NL);
37
+ this.lineOffset += strSplit.length - 1;
38
+ this.columnOffset += strSplit[strSplit.length - 1].length;
39
+ }
31
40
  }
32
41
  if ( writeBuf.length >= 1 ) {
33
42
  this.buf += writeBuf;
@@ -40,15 +49,23 @@ class BundleWriter {
40
49
  writeln(...str) {
41
50
  for ( let i = 0; i < str.length; i++ ) {
42
51
  this.buf += str[i];
52
+ if (str[i] != null && str[i].split) {
53
+ const strSplit = str[i].split(NL);
54
+ this.lineOffset += strSplit.length - 1;
55
+ }
43
56
  }
44
57
  this.buf += NL;
45
58
  this.endsWithNewLine = true;
59
+ this.lineOffset += 1;
60
+ this.columnOffset = 0;
46
61
  }
47
62
 
48
63
  ensureNewLine() {
49
64
  if ( !this.endsWithNewLine ) {
50
65
  this.buf += NL;
51
66
  this.endsWithNewLine = true;
67
+ this.lineOffset += 1;
68
+ this.columnOffset = 0;
52
69
  }
53
70
  }
54
71
 
@@ -1,14 +1,8 @@
1
1
  const Resource = require("./Resource");
2
2
 
3
-
4
- function extractName(path) {
5
- return path.slice( "/resources/".length);
6
- }
7
-
8
-
9
3
  class LocatorResource extends Resource {
10
- constructor(pool, resource) {
11
- super(pool, extractName(resource.getPath()), null, resource.getStatInfo());
4
+ constructor(pool, resource, moduleName) {
5
+ super(pool, moduleName, null, resource.getStatInfo());
12
6
  this.resource = resource;
13
7
  }
14
8
 
@@ -19,6 +13,10 @@ class LocatorResource extends Resource {
19
13
  getProject() {
20
14
  return this.resource._project;
21
15
  }
16
+
17
+ getPath() {
18
+ return this.resource.getPath();
19
+ }
22
20
  }
23
21
 
24
22
  module.exports = LocatorResource;
@@ -2,12 +2,16 @@ const ResourcePool = require("./ResourcePool");
2
2
  const LocatorResource = require("./LocatorResource");
3
3
 
4
4
  class LocatorResourcePool extends ResourcePool {
5
- prepare(resources) {
5
+ prepare(resources, moduleNameMapping) {
6
6
  resources = resources.filter( (res) => !res.getStatInfo().isDirectory() );
7
7
  return Promise.all(
8
- resources.map(
9
- (resource) => this.addResource( new LocatorResource(this, resource) )
10
- ).filter(Boolean)
8
+ resources.map((resource) => {
9
+ let moduleName = moduleNameMapping && moduleNameMapping[resource.getPath()];
10
+ if (!moduleName) {
11
+ moduleName = resource.getPath().slice("/resources/".length);
12
+ }
13
+ return this.addResource(new LocatorResource(this, resource, moduleName));
14
+ }).filter(Boolean)
11
15
  );
12
16
  }
13
17
  }
@@ -18,6 +18,13 @@ class Resource {
18
18
  async buffer() {
19
19
  return readFile(this.file);
20
20
  }
21
+
22
+ /**
23
+ * @returns {Promise<string>} String of the file content
24
+ */
25
+ async string() {
26
+ return (await this.buffer()).toString();
27
+ }
21
28
  }
22
29
 
23
30
  module.exports = Resource;
@@ -193,17 +193,16 @@ class ResourceCollector {
193
193
  }
194
194
 
195
195
  async determineResourceDetails({
196
- debugResources, mergedResources, designtimeResources, supportResources, debugBundles
196
+ debugResources, mergedResources, designtimeResources, supportResources
197
197
  }) {
198
198
  const baseNames = new Set();
199
199
  const debugFilter = new ResourceFilterList(debugResources);
200
200
  const mergeFilter = new ResourceFilterList(mergedResources);
201
201
  const designtimeFilter = new ResourceFilterList(designtimeResources);
202
202
  const supportFilter = new ResourceFilterList(supportResources);
203
- const debugBundleFilter = new ResourceFilterList(debugBundles);
204
203
 
205
204
  const promises = [];
206
- const nonBundledDebugResources = [];
205
+ const debugResourcesInfo = [];
207
206
 
208
207
  for (const [name, info] of this._resources.entries()) {
209
208
  if ( debugFilter.matches(name) ) {
@@ -231,13 +230,14 @@ class ResourceCollector {
231
230
  }
232
231
 
233
232
  if ( /(?:\.js|\.view\.xml|\.control\.xml|\.fragment\.xml)$/.test(name) ) {
234
- if ( (!info.isDebug || debugBundleFilter.matches(name)) ) {
235
- // Only analyze non-debug files and special debug bundles (like sap-ui-core-dbg.js)
233
+ if ( !info.isDebug ) {
234
+ // Only analyze non-dbg files in first run
236
235
  promises.push(
237
236
  this.enrichWithDependencyInfo(info)
238
237
  );
239
238
  } else {
240
- nonBundledDebugResources.push(info);
239
+ // Collect dbg files to be handled in a second step (see below)
240
+ debugResourcesInfo.push(info);
241
241
  }
242
242
  }
243
243
 
@@ -279,19 +279,37 @@ class ResourceCollector {
279
279
 
280
280
  await Promise.all(promises);
281
281
 
282
- for (let i = nonBundledDebugResources.length - 1; i >= 0; i--) {
283
- const dbgInfo = nonBundledDebugResources[i];
282
+ const debugBundlePromises = [];
283
+
284
+ for (let i = debugResourcesInfo.length - 1; i >= 0; i--) {
285
+ const dbgInfo = debugResourcesInfo[i];
284
286
  const nonDebugName = ResourceInfoList.getNonDebugName(dbgInfo.name);
285
287
  const nonDbgInfo = this._resources.get(nonDebugName);
286
- const newDbgInfo = new ResourceInfo(dbgInfo.name);
287
-
288
- // First copy info of analysis from non-dbg file (included, required, condRequired, ...)
289
- newDbgInfo.copyFrom(null, nonDbgInfo);
290
- // Then copy over info from dbg file to properly set name, isDebug, etc.
291
- newDbgInfo.copyFrom(null, dbgInfo);
292
288
 
293
- this._resources.set(dbgInfo.name, newDbgInfo);
289
+ // FIXME: "merged" property is only calculated in ResourceInfo#copyFrom
290
+ // Therefore using the same logic here to compute it.
291
+ if (!nonDbgInfo || (nonDbgInfo.included != null && nonDbgInfo.included.size > 0)) {
292
+ // We need to analyze the dbg resource if there is no non-dbg variant or
293
+ // it is a bundle because we will (usually) have different content.
294
+ debugBundlePromises.push(
295
+ this.enrichWithDependencyInfo(dbgInfo)
296
+ );
297
+ } else {
298
+ // If the non-dbg resource is not a bundle, we can just copy over the info and skip
299
+ // analyzing the dbg variant as both should have the same info.
300
+
301
+ const newDbgInfo = new ResourceInfo(dbgInfo.name);
302
+
303
+ // First copy info of analysis from non-dbg file (included, required, condRequired, ...)
304
+ newDbgInfo.copyFrom(null, nonDbgInfo);
305
+ // Then copy over info from dbg file to properly set name, isDebug, etc.
306
+ newDbgInfo.copyFrom(null, dbgInfo);
307
+
308
+ this._resources.set(dbgInfo.name, newDbgInfo);
309
+ }
294
310
  }
311
+
312
+ await Promise.all(debugBundlePromises);
295
313
  }
296
314
 
297
315
  createOrphanFilters() {
@@ -9,7 +9,7 @@ function parseJS(code, userOptions = {}) {
9
9
  // allowed options and their defaults
10
10
  const options = {
11
11
  comment: false,
12
- ecmaVersion: 2020,
12
+ ecmaVersion: 2021, // NOTE: Adopt JSModuleAnalyzer.js to allow new Syntax when upgrading to newer ECMA versions
13
13
  range: false,
14
14
  sourceType: "script",
15
15
  };
@@ -89,7 +89,8 @@ const log = require("@ui5/logger").getLogger("builder:processors:bundlers:module
89
89
  *
90
90
  * @public
91
91
  * @typedef {object} ModuleBundleOptions
92
- * @property {boolean} [optimize=false] If set to 'true' the module bundle gets minified
92
+ * @property {boolean} [optimize=true] Whether the module bundle gets minified
93
+ * @property {boolean} [sourceMap=true] Whether to generate a source map file for the bundle
93
94
  * @property {boolean} [decorateBootstrapModule=false] If set to 'false', the module won't be decorated
94
95
  * with an optimization marker
95
96
  * @property {boolean} [addTryCatchRestartWrapper=false] Whether to wrap bootable module bundles with
@@ -101,23 +102,41 @@ const log = require("@ui5/logger").getLogger("builder:processors:bundlers:module
101
102
  */
102
103
 
103
104
  /**
104
- * Legacy preload bundler.
105
+ * Result set
106
+ *
107
+ * @public
108
+ * @typedef {object} ModuleBundlerResult
109
+ * @property {module:@ui5/fs.Resource} bundle Bundle resource
110
+ * @property {module:@ui5/fs.Resource} sourceMap Source Map
111
+ * @memberof module:@ui5/builder.processors
112
+ */
113
+
114
+ /**
115
+ * Legacy module bundler.
105
116
  *
106
117
  * @public
107
118
  * @alias module:@ui5/builder.processors.moduleBundler
108
119
  * @param {object} parameters Parameters
109
120
  * @param {module:@ui5/fs.Resource[]} parameters.resources Resources
110
121
  * @param {object} parameters.options Options
122
+ * @param {object} [parameters.options.moduleNameMapping]
123
+ Optional mapping of resource paths to module name in order to overwrite the default determination
111
124
  * @param {ModuleBundleDefinition} parameters.options.bundleDefinition Module bundle definition
112
- * @param {ModuleBundleOptions} parameters.options.bundleOptions Module bundle options
113
- * @returns {Promise<module:@ui5/fs.Resource[]>} Promise resolving with module bundle resources
125
+ * @param {ModuleBundleOptions} [parameters.options.bundleOptions] Module bundle options
126
+ * @returns {Promise<module:@ui5/builder.processors.ModuleBundlerResult[]>}
127
+ * Promise resolving with module bundle resources
114
128
  */
115
- module.exports = function({resources, options: {bundleDefinition, bundleOptions}}) {
116
- // console.log("preloadBundler bundleDefinition:");
117
- // console.log(JSON.stringify(options.bundleDefinition, null, 4));
118
-
119
- // TODO 3.0: Fix defaulting behavior, align with JSDoc
120
- bundleOptions = bundleOptions || {optimize: true};
129
+ module.exports = function({resources, options: {bundleDefinition, bundleOptions, moduleNameMapping}}) {
130
+ // Apply defaults without modifying the passed object
131
+ bundleOptions = Object.assign({}, {
132
+ optimize: true,
133
+ sourceMap: true,
134
+ decorateBootstrapModule: false,
135
+ addTryCatchRestartWrapper: false,
136
+ usePredefineCalls: false,
137
+ numberOfParts: 1,
138
+ ignoreMissingModules: false
139
+ }, bundleOptions);
121
140
 
122
141
  const pool = new LocatorResourcePool({
123
142
  ignoreMissingModules: bundleOptions.ignoreMissingModules
@@ -130,7 +149,7 @@ module.exports = function({resources, options: {bundleDefinition, bundleOptions}
130
149
  log.verbose(`bundleOptions: ${JSON.stringify(bundleOptions, null, 2)}`);
131
150
  }
132
151
 
133
- return pool.prepare( resources ).
152
+ return pool.prepare( resources, moduleNameMapping ).
134
153
  then( () => builder.createBundle(bundleDefinition, bundleOptions) ).
135
154
  then( (results) => {
136
155
  let bundles;
@@ -142,13 +161,20 @@ module.exports = function({resources, options: {bundleDefinition, bundleOptions}
142
161
 
143
162
  return Promise.all(bundles.map((bundleObj) => {
144
163
  if ( bundleObj ) {
145
- const {name, content} = bundleObj;
164
+ const {name, content, sourceMap} = bundleObj;
146
165
  // console.log("creating bundle as '%s'", "/resources/" + name);
147
- const resource = new EvoResource({
166
+ const res = {};
167
+ res.bundle = new EvoResource({
148
168
  path: "/resources/" + name,
149
169
  string: content
150
170
  });
151
- return resource;
171
+ if (sourceMap) {
172
+ res.sourceMap = new EvoResource({
173
+ path: "/resources/" + name + ".map",
174
+ string: sourceMap
175
+ });
176
+ }
177
+ return res;
152
178
  }
153
179
  }));
154
180
  });
@@ -0,0 +1,90 @@
1
+ const path = require("path");
2
+ const terser = require("terser");
3
+ const Resource = require("@ui5/fs").Resource;
4
+ /**
5
+ * Preserve comments which contain:
6
+ * <ul>
7
+ * <li>copyright notice</li>
8
+ * <li>license terms</li>
9
+ * <li>"@ui5-bundle"</li>
10
+ * <li>"@ui5-bundle-raw-include"</li>
11
+ * </ul>
12
+ *
13
+ * @type {RegExp}
14
+ */
15
+ const copyrightCommentsAndBundleCommentPattern = /copyright|\(c\)(?:[0-9]+|\s+[0-9A-za-z])|released under|license|\u00a9|^@ui5-bundle-raw-include |^@ui5-bundle /i;
16
+ const debugFileRegex = /((?:\.view|\.fragment|\.controller|\.designtime|\.support)?\.js)$/;
17
+
18
+
19
+ /**
20
+ * Result set
21
+ *
22
+ * @public
23
+ * @typedef {object} MinifierResult
24
+ * @property {module:@ui5/fs.Resource} resource Minified resource
25
+ * @property {module:@ui5/fs.Resource} dbgResource Debug (non-minified) variant
26
+ * @property {module:@ui5/fs.Resource} sourceMap Source Map
27
+ * @memberof module:@ui5/builder.processors
28
+ */
29
+
30
+ /**
31
+ * Minifies the supplied resources.
32
+ *
33
+ * @public
34
+ * @alias module:@ui5/builder.processors.minifier
35
+ * @param {object} parameters Parameters
36
+ * @param {module:@ui5/fs.Resource[]} parameters.resources List of resources to be processed
37
+ * @param {object} [parameters.options] Options
38
+ * @param {boolean} [parameters.options.addSourceMappingUrl=true]
39
+ * Whether to add a sourceMappingURL reference to the end of the minified resource
40
+ * @returns {Promise<module:@ui5/builder.processors.MinifierResult[]>}
41
+ * Promise resolving with object of resource, dbgResource and sourceMap
42
+ */
43
+ module.exports = async function({resources, options: {addSourceMappingUrl = true} = {}}) {
44
+ return Promise.all(resources.map(async (resource) => {
45
+ const dbgPath = resource.getPath().replace(debugFileRegex, "-dbg$1");
46
+ const dbgResource = await resource.clone();
47
+ dbgResource.setPath(dbgPath);
48
+
49
+ const filename = path.posix.basename(resource.getPath());
50
+ const code = await resource.getString();
51
+ try {
52
+ const sourceMapOptions = {
53
+ filename
54
+ };
55
+ if (addSourceMappingUrl) {
56
+ sourceMapOptions.url = filename + ".map";
57
+ }
58
+ const dbgFilename = path.posix.basename(dbgPath);
59
+ const result = await terser.minify({
60
+ // Use debug-name since this will be referenced in the source map "sources"
61
+ [dbgFilename]: code
62
+ }, {
63
+ output: {
64
+ comments: copyrightCommentsAndBundleCommentPattern,
65
+ wrap_func_args: false
66
+ },
67
+ compress: false,
68
+ mangle: {
69
+ reserved: [
70
+ "jQuery",
71
+ "jquery",
72
+ "sap",
73
+ ]
74
+ },
75
+ sourceMap: sourceMapOptions
76
+ });
77
+ resource.setString(result.code);
78
+ const sourceMapResource = new Resource({
79
+ path: resource.getPath() + ".map",
80
+ string: result.map
81
+ });
82
+ return {resource, dbgResource, sourceMapResource};
83
+ } catch (err) {
84
+ // Note: err.filename contains the debug-name
85
+ throw new Error(
86
+ `Minification failed with error: ${err.message} in file ${filename} ` +
87
+ `(line ${err.line}, col ${err.col}, pos ${err.pos})`);
88
+ }
89
+ }));
90
+ };
@@ -66,17 +66,6 @@ const DEFAULT_SUPPORT_RESOURCES_FILTER = [
66
66
  "**/*.support.js"
67
67
  ];
68
68
 
69
- /**
70
- * Hard coded debug bundle, to trigger separate analysis for this filename
71
- * because sap-ui-core.js and sap-ui-core-dbg.js have different includes
72
- *
73
- * @type {string[]}
74
- */
75
- const DEBUG_BUNDLES = [
76
- "sap-ui-core-dbg.js",
77
- "sap-ui-core-nojQuery-dbg.js"
78
- ];
79
-
80
69
  /**
81
70
  * Creates and adds resources.json entry (itself) to the list.
82
71
  *
@@ -138,8 +127,7 @@ module.exports = async function({resources, dependencyResources = [], options})
138
127
  debugResources: DEFAULT_DEBUG_RESOURCES_FILTER,
139
128
  mergedResources: DEFAULT_BUNDLE_RESOURCES_FILTER,
140
129
  designtimeResources: DEFAULT_DESIGNTIME_RESOURCES_FILTER,
141
- supportResources: DEFAULT_SUPPORT_RESOURCES_FILTER,
142
- debugBundles: DEBUG_BUNDLES
130
+ supportResources: DEFAULT_SUPPORT_RESOURCES_FILTER
143
131
  }, options);
144
132
 
145
133
  const pool = new LocatorResourcePool();
@@ -158,12 +146,10 @@ module.exports = async function({resources, dependencyResources = [], options})
158
146
  }
159
147
 
160
148
  await collector.determineResourceDetails({
161
- pool,
162
149
  debugResources: options.debugResources,
163
150
  mergedResources: options.mergedResources,
164
151
  designtimeResources: options.designtimeResources,
165
- supportResources: options.supportResources,
166
- debugBundles: options.debugBundles
152
+ supportResources: options.supportResources
167
153
  });
168
154
 
169
155
  // group resources by components and create ResourceInfoLists
@@ -51,14 +51,14 @@ class TaskUtil {
51
51
  * This method is only available to custom task extensions defining
52
52
  * <b>Specification Version 2.2 and above</b>.
53
53
  *
54
- * @param {module:@ui5/fs.Resource} resource The resource the tag should be stored for
54
+ * @param {string|module:@ui5/fs.Resource} resourcePath Path or resource-instance the tag should be stored for
55
55
  * @param {string} tag Name of the tag.
56
56
  * Currently only the [STANDARD_TAGS]{@link module:@ui5/builder.tasks.TaskUtil#STANDARD_TAGS} are allowed
57
57
  * @param {string|boolean|integer} [value=true] Tag value. Must be primitive
58
58
  * @public
59
59
  */
60
- setTag(resource, tag, value) {
61
- return this._projectBuildContext.getResourceTagCollection().setTag(resource, tag, value);
60
+ setTag(resourcePath, tag, value) {
61
+ return this._projectBuildContext.getResourceTagCollection().setTag(resourcePath, tag, value);
62
62
  }
63
63
 
64
64
  /**
@@ -68,14 +68,14 @@ class TaskUtil {
68
68
  * This method is only available to custom task extensions defining
69
69
  * <b>Specification Version 2.2 and above</b>.
70
70
  *
71
- * @param {module:@ui5/fs.Resource} resource The resource the tag should be retrieved for
71
+ * @param {string|module:@ui5/fs.Resource} resourcePath Path or resource-instance the tag should be retrieved for
72
72
  * @param {string} tag Name of the tag
73
73
  * @returns {string|boolean|integer|undefined} Tag value for the given resource.
74
74
  * <code>undefined</code> if no value is available
75
75
  * @public
76
76
  */
77
- getTag(resource, tag) {
78
- return this._projectBuildContext.getResourceTagCollection().getTag(resource, tag);
77
+ getTag(resourcePath, tag) {
78
+ return this._projectBuildContext.getResourceTagCollection().getTag(resourcePath, tag);
79
79
  }
80
80
 
81
81
  /**
@@ -86,12 +86,12 @@ class TaskUtil {
86
86
  * This method is only available to custom task extensions defining
87
87
  * <b>Specification Version 2.2 and above</b>.
88
88
  *
89
- * @param {module:@ui5/fs.Resource} resource The resource the tag should be cleared for
89
+ * @param {string|module:@ui5/fs.Resource} resourcePath Path or resource-instance the tag should be cleared for
90
90
  * @param {string} tag Tag
91
91
  * @public
92
92
  */
93
- clearTag(resource, tag) {
94
- return this._projectBuildContext.getResourceTagCollection().clearTag(resource, tag);
93
+ clearTag(resourcePath, tag) {
94
+ return this._projectBuildContext.getResourceTagCollection().clearTag(resourcePath, tag);
95
95
  }
96
96
 
97
97
  /**
@@ -1,4 +1,5 @@
1
1
  const moduleBundler = require("../../processors/bundlers/moduleBundler");
2
+ const createModuleNameMapping = require("./utils/createModuleNameMapping");
2
3
  const ReaderCollectionPrioritized = require("@ui5/fs").ReaderCollectionPrioritized;
3
4
 
4
5
  /**
@@ -13,30 +14,97 @@ const ReaderCollectionPrioritized = require("@ui5/fs").ReaderCollectionPrioritiz
13
14
  * @param {object} parameters.options Options
14
15
  * @param {string} parameters.options.projectName Project name
15
16
  * @param {ModuleBundleDefinition} parameters.options.bundleDefinition Module bundle definition
16
- * @param {ModuleBundleOptions} parameters.options.bundleOptions Module bundle options
17
+ * @param {ModuleBundleOptions} [parameters.options.bundleOptions] Module bundle options
17
18
  * @returns {Promise} Promise resolving with <code>undefined</code> once data has been written
18
19
  */
19
20
  module.exports = function({
20
21
  workspace, dependencies, taskUtil, options: {projectName, bundleDefinition, bundleOptions}
21
22
  }) {
22
- const combo = new ReaderCollectionPrioritized({
23
- name: `libraryBundler - prioritize workspace over dependencies: ${projectName}`,
23
+ let combo = new ReaderCollectionPrioritized({
24
+ name: `generateBundle - prioritize workspace over dependencies: ${projectName}`,
24
25
  readers: [workspace, dependencies]
25
26
  });
26
27
 
27
- return combo.byGlob("/resources/**/*.{js,json,xml,html,properties,library}").then((resources) => {
28
- return moduleBundler({
29
- options: {
30
- bundleDefinition,
31
- bundleOptions
32
- },
33
- resources
34
- }).then((bundles) => {
35
- return Promise.all(bundles.map((bundle) => {
28
+ const optimize = !bundleOptions || bundleOptions.optimize !== false;
29
+ if (taskUtil) {
30
+ /* Scenarios
31
+ 1. Optimize bundle with minification already done
32
+ Workspace:
33
+ * /resources/my/lib/Control.js [ui5:HasDebugVariant]
34
+ * /resources/my/lib/Control.js.map [ui5:HasDebugVariant]
35
+ * /resources/my/lib/Control-dbg.js [ui5:IsDebugVariant]
36
+
37
+ Bundler input:
38
+ * /resources/my/lib/Control.js
39
+ * /resources/my/lib/Control.js.map
40
+
41
+ => Filter out debug resources
42
+
43
+ 2. Optimize bundle with no minification
44
+ * /resources/my/lib/Control.js
45
+
46
+ => No action necessary
47
+
48
+ 3. Debug-bundle with minification already done
49
+ Workspace:
50
+ * /resources/my/lib/Control.js [ui5:HasDebugVariant]
51
+ * /resources/my/lib/Control.js.map [ui5:HasDebugVariant]
52
+ * /resources/my/lib/Control-dbg.js [ui5:IsDebugVariant]
53
+
54
+ Bundler input:
55
+ * /resources/my/lib/Control-dbg.js
56
+ * moduleNameMapping: [{"/resources/my/lib/Control-dbg.js": "my/lib/Control.js"}]
57
+
58
+ => Filter out minified-resources (tagged as "HasDebugVariant", incl. source maps) and rename debug-files
59
+
60
+ 4. Debug-bundle with no minification
61
+ * /resources/my/lib/Control.js
62
+
63
+ => No action necessary
64
+
65
+ 5. Bundle with external input (optimize or not), e.g. TS-project
66
+ Workspace:
67
+ * /resources/my/lib/Control.ts
68
+ * /resources/my/lib/Control.js
69
+ * /resources/my/lib/Control.js.map
70
+
71
+ Bundler input:
72
+ * /resources/my/lib/Control.js
73
+ * /resources/my/lib/Control.js.map
74
+ */
75
+
76
+ // Omit -dbg files for optimize bundles and vice versa
77
+ const filterTag = optimize ?
78
+ taskUtil.STANDARD_TAGS.IsDebugVariant : taskUtil.STANDARD_TAGS.HasDebugVariant;
79
+ combo = combo.filter(function(resource) {
80
+ return !taskUtil.getTag(resource, filterTag);
81
+ });
82
+ }
83
+
84
+ return combo.byGlob("/resources/**/*.{js,json,xml,html,properties,library,js.map}").then((resources) => {
85
+ const options = {bundleDefinition, bundleOptions};
86
+ if (!optimize && taskUtil) {
87
+ options.moduleNameMapping = createModuleNameMapping({resources, taskUtil});
88
+ }
89
+ return moduleBundler({options, resources}).then((bundles) => {
90
+ return Promise.all(bundles.map(({bundle, sourceMap} = {}) => {
91
+ if (!bundle) {
92
+ // Skip empty bundles
93
+ return;
94
+ }
36
95
  if (taskUtil) {
37
96
  taskUtil.setTag(bundle, taskUtil.STANDARD_TAGS.IsBundle);
97
+ if (sourceMap) {
98
+ // Clear tag that might have been set by the minify task, in cases where
99
+ // the bundle name is identical to a source file
100
+ taskUtil.clearTag(sourceMap, taskUtil.STANDARD_TAGS.OmitFromBuildResult);
101
+ }
102
+ }
103
+ const writes = [workspace.write(bundle)];
104
+ if (sourceMap) {
105
+ writes.push(workspace.write(sourceMap));
38
106
  }
39
- return workspace.write(bundle);
107
+ return Promise.all(writes);
40
108
  }));
41
109
  });
42
110
  });
@@ -28,13 +28,20 @@ const {negateFilters} = require("../../lbt/resources/ResourceFilterList");
28
28
  module.exports = function({
29
29
  workspace, dependencies, taskUtil, options: {projectName, paths, namespaces, excludes = []}
30
30
  }) {
31
- const combo = new ReaderCollectionPrioritized({
31
+ let combo = new ReaderCollectionPrioritized({
32
32
  name: `generateComponentPreload - prioritize workspace over dependencies: ${projectName}`,
33
33
  readers: [workspace, dependencies]
34
34
  });
35
35
 
36
+ if (taskUtil) {
37
+ combo = combo.filter(function(resource) {
38
+ // Remove any debug variants
39
+ return !taskUtil.getTag(resource, taskUtil.STANDARD_TAGS.IsDebugVariant);
40
+ });
41
+ }
42
+
36
43
  // TODO 3.0: Limit to workspace resources?
37
- return combo.byGlob("/resources/**/*.{js,json,xml,html,properties,library}")
44
+ return combo.byGlob("/resources/**/*.{js,json,xml,html,properties,library,js.map}")
38
45
  .then(async (resources) => {
39
46
  let allNamespaces = [];
40
47
  if (paths) {
@@ -141,12 +148,19 @@ module.exports = function({
141
148
  });
142
149
  }));
143
150
  })
144
- .then((processedResources) => {
145
- return Promise.all(processedResources.map((resource) => {
151
+ .then((results) => {
152
+ const bundles = Array.prototype.concat.apply([], results);
153
+ return Promise.all(bundles.map(({bundle, sourceMap}) => {
146
154
  if (taskUtil) {
147
- taskUtil.setTag(resource[0], taskUtil.STANDARD_TAGS.IsBundle);
155
+ taskUtil.setTag(bundle, taskUtil.STANDARD_TAGS.IsBundle);
156
+ // Clear tag that might have been set by the minify task, in cases where
157
+ // the bundle name is identical to a source file
158
+ taskUtil.clearTag(sourceMap, taskUtil.STANDARD_TAGS.OmitFromBuildResult);
148
159
  }
149
- return workspace.write(resource[0]);
160
+ return Promise.all([
161
+ workspace.write(bundle),
162
+ workspace.write(sourceMap)
163
+ ]);
150
164
  }));
151
165
  });
152
166
  };
@@ -1,5 +1,6 @@
1
1
  const log = require("@ui5/logger").getLogger("builder:tasks:bundlers:generateFlexChangesBundle");
2
2
  const flexChangesBundler = require("../../processors/bundlers/flexChangesBundler");
3
+ const semver = require("semver");
3
4
 
4
5
  /**
5
6
  * Task to create changesBundle.json file containing all changes stored in the /changes folder for easier consumption
@@ -68,9 +69,9 @@ module.exports = async function({workspace, taskUtil, options: {namespace}}) {
68
69
  const allResources = await workspace.byGlob(
69
70
  `${pathPrefix}/changes/*.{change,variant,ctrl_variant,ctrl_variant_change,ctrl_variant_management_change}`);
70
71
  if (allResources.length > 0) {
71
- const version = await readManifestMinUI5Version();
72
+ const version = semver.coerce(await readManifestMinUI5Version());
72
73
  let hasFlexBundleVersion = false;
73
- if (parseFloat(version) >= 1.73) {
74
+ if (semver.compare(version, "1.73.0") >= 0) {
74
75
  hasFlexBundleVersion = true;
75
76
  }
76
77
  const processedResources = await flexChangesBundler({