@ui5/builder 2.11.4 → 3.0.0-alpha.2

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 +52 -9
  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/bundle/Resolver.js +1 -1
  13. package/lib/lbt/resources/LocatorResource.js +9 -7
  14. package/lib/lbt/resources/LocatorResourcePool.js +8 -4
  15. package/lib/lbt/resources/Resource.js +7 -0
  16. package/lib/lbt/resources/ResourceCollector.js +33 -15
  17. package/lib/lbt/utils/parseUtils.js +1 -1
  18. package/lib/processors/bundlers/moduleBundler.js +39 -14
  19. package/lib/processors/minifier.js +89 -0
  20. package/lib/processors/resourceListCreator.js +2 -16
  21. package/lib/tasks/TaskUtil.js +9 -9
  22. package/lib/tasks/bundlers/generateBundle.js +92 -6
  23. package/lib/tasks/bundlers/generateComponentPreload.js +20 -6
  24. package/lib/tasks/bundlers/generateLibraryPreload.js +77 -10
  25. package/lib/tasks/bundlers/generateStandaloneAppBundle.js +58 -12
  26. package/lib/tasks/generateResourcesJson.js +14 -8
  27. package/lib/tasks/minify.js +39 -0
  28. package/lib/tasks/replaceVersion.js +1 -1
  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 +15 -13
  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
 
@@ -289,7 +289,7 @@ class BundleResolver {
289
289
 
290
290
  return collectModulesForSection(section).
291
291
  then( (modules) => {
292
- if ( section.mode == SectionType.Raw && section.sort ) {
292
+ if ( section.mode == SectionType.Raw && section.sort !== false ) {
293
293
  // sort the modules in topological order
294
294
  return topologicalSort(pool, modules).then( (modules) => {
295
295
  log.verbose(" resolved modules (sorted): %s", modules);
@@ -1,14 +1,12 @@
1
1
  const Resource = require("./Resource");
2
2
 
3
-
4
- function extractName(path) {
5
- return path.slice( "/resources/".length);
6
- }
7
-
3
+ // function extractName(path) {
4
+ // return path.slice( "/resources/".length);
5
+ // }
8
6
 
9
7
  class LocatorResource extends Resource {
10
- constructor(pool, resource) {
11
- super(pool, extractName(resource.getPath()), null, resource.getStatInfo());
8
+ constructor(pool, resource, moduleName) {
9
+ super(pool, moduleName, null, resource.getStatInfo());
12
10
  this.resource = resource;
13
11
  }
14
12
 
@@ -19,6 +17,10 @@ class LocatorResource extends Resource {
19
17
  getProject() {
20
18
  return this.resource._project;
21
19
  }
20
+
21
+ getPath() {
22
+ return this.resource.getPath();
23
+ }
22
24
  }
23
25
 
24
26
  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
+ 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,9 @@ 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] Whether to generate a source map file for the bundle.
94
+ * Defaults to true if <code>optimize</code> is set to true
93
95
  * @property {boolean} [decorateBootstrapModule=false] If set to 'false', the module won't be decorated
94
96
  * with an optimization marker
95
97
  * @property {boolean} [addTryCatchRestartWrapper=false] Whether to wrap bootable module bundles with
@@ -101,23 +103,39 @@ const log = require("@ui5/logger").getLogger("builder:processors:bundlers:module
101
103
  */
102
104
 
103
105
  /**
104
- * Legacy preload bundler.
106
+ * Result set
107
+ *
108
+ * @public
109
+ * @typedef {object} ModuleBundlerResult
110
+ * @property {module:@ui5/fs.Resource} bundle Bundle resource
111
+ * @property {module:@ui5/fs.Resource} sourceMap Source Map
112
+ * @memberof module:@ui5/builder.processors
113
+ */
114
+
115
+ /**
116
+ * Legacy module bundler.
105
117
  *
106
118
  * @public
107
119
  * @alias module:@ui5/builder.processors.moduleBundler
108
120
  * @param {object} parameters Parameters
109
121
  * @param {module:@ui5/fs.Resource[]} parameters.resources Resources
110
122
  * @param {object} parameters.options Options
123
+ * @param {object} [parameters.options.moduleNameMapping]
124
+ Optional mapping of resource paths to module name in order to overwrite the default determination
111
125
  * @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
126
+ * @param {ModuleBundleOptions} [parameters.options.bundleOptions] Module bundle options
127
+ * @returns {Promise<module:@ui5/builder.processors.MinifierResult[]>} 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
+ decorateBootstrapModule: false,
134
+ addTryCatchRestartWrapper: false,
135
+ usePredefineCalls: false,
136
+ numberOfParts: 1,
137
+ ignoreMissingModules: false
138
+ }, bundleOptions);
121
139
 
122
140
  const pool = new LocatorResourcePool({
123
141
  ignoreMissingModules: bundleOptions.ignoreMissingModules
@@ -130,7 +148,7 @@ module.exports = function({resources, options: {bundleDefinition, bundleOptions}
130
148
  log.verbose(`bundleOptions: ${JSON.stringify(bundleOptions, null, 2)}`);
131
149
  }
132
150
 
133
- return pool.prepare( resources ).
151
+ return pool.prepare( resources, moduleNameMapping ).
134
152
  then( () => builder.createBundle(bundleDefinition, bundleOptions) ).
135
153
  then( (results) => {
136
154
  let bundles;
@@ -142,13 +160,20 @@ module.exports = function({resources, options: {bundleDefinition, bundleOptions}
142
160
 
143
161
  return Promise.all(bundles.map((bundleObj) => {
144
162
  if ( bundleObj ) {
145
- const {name, content} = bundleObj;
163
+ const {name, content, sourceMap} = bundleObj;
146
164
  // console.log("creating bundle as '%s'", "/resources/" + name);
147
- const resource = new EvoResource({
165
+ const res = {};
166
+ res.bundle = new EvoResource({
148
167
  path: "/resources/" + name,
149
168
  string: content
150
169
  });
151
- return resource;
170
+ if (sourceMap) {
171
+ res.sourceMap = new EvoResource({
172
+ path: "/resources/" + name + ".map",
173
+ string: sourceMap
174
+ });
175
+ }
176
+ return res;
152
177
  }
153
178
  }));
154
179
  });
@@ -0,0 +1,89 @@
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 {boolean} [parameters.addSourceMappingUrl=true]
38
+ * Whether to add a sourceMappingURL reference to the end of the minified resource
39
+ * @returns {Promise<module:@ui5/builder.processors.MinifierResult[]>}
40
+ * Promise resolving with object of resource, dbgResource and sourceMap
41
+ */
42
+ module.exports = async function({resources, addSourceMappingUrl = true}) {
43
+ return Promise.all(resources.map(async (resource) => {
44
+ const dbgPath = resource.getPath().replace(debugFileRegex, "-dbg$1");
45
+ const dbgResource = await resource.clone();
46
+ dbgResource.setPath(dbgPath);
47
+
48
+ const filename = path.posix.basename(resource.getPath());
49
+ const code = await resource.getString();
50
+ try {
51
+ const sourceMapOptions = {
52
+ filename
53
+ };
54
+ if (addSourceMappingUrl) {
55
+ sourceMapOptions.url = filename + ".map";
56
+ }
57
+ const dbgFilename = path.posix.basename(dbgPath);
58
+ const result = await terser.minify({
59
+ // Use debug-name since this will be referenced in the source map "sources"
60
+ [dbgFilename]: code
61
+ }, {
62
+ output: {
63
+ comments: copyrightCommentsAndBundleCommentPattern,
64
+ wrap_func_args: false
65
+ },
66
+ compress: false,
67
+ mangle: {
68
+ reserved: [
69
+ "jQuery",
70
+ "jquery",
71
+ "sap",
72
+ ]
73
+ },
74
+ sourceMap: sourceMapOptions
75
+ });
76
+ resource.setString(result.code);
77
+ const sourceMapResource = new Resource({
78
+ path: resource.getPath() + ".map",
79
+ string: result.map
80
+ });
81
+ return {resource, dbgResource, sourceMapResource};
82
+ } catch (err) {
83
+ // Note: err.filename contains the debug-name
84
+ throw new Error(
85
+ `Minification failed with error: ${err.message} in file ${filename} ` +
86
+ `(line ${err.line}, col ${err.col}, pos ${err.pos})`);
87
+ }
88
+ }));
89
+ };
@@ -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 ModuleName = require("../../lbt/utils/ModuleName");
2
3
  const ReaderCollectionPrioritized = require("@ui5/fs").ReaderCollectionPrioritized;
3
4
 
4
5
  /**
@@ -13,30 +14,115 @@ 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
+ let combo = new ReaderCollectionPrioritized({
23
24
  name: `libraryBundler - 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
+ 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 moduleNameMapping = {};
86
+ if (!optimize && taskUtil) {
87
+ // For "unoptimized" bundles, the non-debug files have already been filtered out above.
88
+ // Now we need to create a mapping from the debug-variant resource path to the respective module name,
89
+ // which is basically the non-debug resource path, minus the "/resources/"" prefix.
90
+ // This mapping overwrites internal logic of the LocatorResourcePool which would otherwise determine
91
+ // the module name from the resource path, which would contain "-dbg" in this case. That would be
92
+ // incorrect since debug-variants should still keep the original module name.
93
+ for (let i = resources.length - 1; i >= 0; i--) {
94
+ const resourcePath = resources[i].getPath();
95
+ if (taskUtil.getTag(resourcePath, taskUtil.STANDARD_TAGS.IsDebugVariant)) {
96
+ const nonDbgPath = ModuleName.getNonDebugName(resourcePath);
97
+ if (!nonDbgPath) {
98
+ throw new Error(`Failed to resolve non-debug name for ${resourcePath}`);
99
+ }
100
+ moduleNameMapping[resourcePath] = nonDbgPath.slice("/resources/".length);
101
+ }
102
+ }
103
+ }
28
104
  return moduleBundler({
29
105
  options: {
30
106
  bundleDefinition,
31
- bundleOptions
107
+ bundleOptions,
108
+ moduleNameMapping
32
109
  },
33
110
  resources
34
111
  }).then((bundles) => {
35
- return Promise.all(bundles.map((bundle) => {
112
+ return Promise.all(bundles.map(({bundle, sourceMap}) => {
36
113
  if (taskUtil) {
37
114
  taskUtil.setTag(bundle, taskUtil.STANDARD_TAGS.IsBundle);
115
+ if (sourceMap) {
116
+ // Clear tag that might have been set by the minify task, in cases where
117
+ // the bundle name is identical to a source file
118
+ taskUtil.clearTag(sourceMap, taskUtil.STANDARD_TAGS.OmitFromBuildResult);
119
+ }
120
+ }
121
+ const writes = [workspace.write(bundle)];
122
+ if (sourceMap) {
123
+ writes.push(workspace.write(sourceMap));
38
124
  }
39
- return workspace.write(bundle);
125
+ return Promise.all(writes);
40
126
  }));
41
127
  });
42
128
  });
@@ -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
  };