@ui5/builder 2.11.5 → 3.0.0-alpha.10
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 +125 -12
- package/index.js +4 -59
- package/jsdoc.json +0 -1
- package/lib/lbt/analyzer/JSModuleAnalyzer.js +31 -5
- package/lib/lbt/analyzer/XMLTemplateAnalyzer.js +2 -1
- package/lib/lbt/bundle/AutoSplitter.js +10 -24
- package/lib/lbt/bundle/Builder.js +367 -168
- package/lib/lbt/bundle/BundleWriter.js +17 -0
- package/lib/lbt/bundle/Resolver.js +3 -3
- package/lib/lbt/resources/LocatorResource.js +7 -9
- package/lib/lbt/resources/LocatorResourcePool.js +8 -4
- package/lib/lbt/resources/Resource.js +7 -0
- package/lib/lbt/resources/ResourceCollector.js +43 -18
- package/lib/lbt/resources/ResourceInfoList.js +0 -1
- package/lib/lbt/resources/ResourcePool.js +7 -6
- package/lib/lbt/utils/escapePropertiesFile.js +3 -6
- package/lib/lbt/utils/parseUtils.js +1 -1
- package/lib/processors/bundlers/moduleBundler.js +42 -17
- package/lib/processors/jsdoc/lib/createIndexFiles.js +1 -1
- package/lib/processors/jsdoc/lib/transformApiJson.js +7 -16
- package/lib/processors/jsdoc/lib/ui5/plugin.js +111 -6
- package/lib/processors/jsdoc/lib/ui5/template/publish.js +112 -91
- package/lib/processors/jsdoc/lib/ui5/template/utils/versionUtil.js +1 -1
- package/lib/processors/manifestCreator.js +8 -45
- package/lib/processors/minifier.js +90 -0
- package/lib/processors/resourceListCreator.js +2 -16
- package/lib/tasks/buildThemes.js +1 -1
- package/lib/tasks/bundlers/generateBundle.js +82 -14
- package/lib/tasks/bundlers/generateComponentPreload.js +31 -17
- package/lib/tasks/bundlers/generateFlexChangesBundle.js +7 -3
- package/lib/tasks/bundlers/generateLibraryPreload.js +127 -79
- package/lib/tasks/bundlers/generateManifestBundle.js +8 -10
- package/lib/tasks/bundlers/generateStandaloneAppBundle.js +54 -15
- package/lib/tasks/bundlers/utils/createModuleNameMapping.js +31 -0
- package/lib/tasks/generateCachebusterInfo.js +7 -3
- package/lib/tasks/generateLibraryManifest.js +6 -8
- package/lib/tasks/generateResourcesJson.js +15 -9
- package/lib/tasks/generateThemeDesignerResources.js +118 -2
- package/lib/tasks/generateVersionInfo.js +5 -5
- package/lib/tasks/jsdoc/generateJsdoc.js +1 -1
- package/lib/tasks/minify.js +41 -0
- package/lib/tasks/replaceVersion.js +1 -1
- package/lib/tasks/taskRepository.js +7 -15
- package/lib/tasks/transformBootstrapHtml.js +6 -1
- package/package.json +20 -19
- package/lib/builder/BuildContext.js +0 -39
- package/lib/builder/ProjectBuildContext.js +0 -55
- package/lib/builder/builder.js +0 -420
- package/lib/processors/debugFileCreator.js +0 -52
- package/lib/processors/resourceCopier.js +0 -24
- package/lib/processors/uglifier.js +0 -45
- package/lib/tasks/TaskUtil.js +0 -160
- package/lib/tasks/createDebugFiles.js +0 -30
- package/lib/tasks/uglify.js +0 -33
- package/lib/types/AbstractBuilder.js +0 -270
- package/lib/types/AbstractFormatter.js +0 -66
- package/lib/types/AbstractUi5Formatter.js +0 -95
- package/lib/types/application/ApplicationBuilder.js +0 -220
- package/lib/types/application/ApplicationFormatter.js +0 -227
- package/lib/types/application/applicationType.js +0 -15
- package/lib/types/library/LibraryBuilder.js +0 -237
- package/lib/types/library/LibraryFormatter.js +0 -519
- package/lib/types/library/libraryType.js +0 -15
- package/lib/types/module/ModuleBuilder.js +0 -7
- package/lib/types/module/ModuleFormatter.js +0 -54
- package/lib/types/module/moduleType.js +0 -15
- package/lib/types/themeLibrary/ThemeLibraryBuilder.js +0 -62
- package/lib/types/themeLibrary/ThemeLibraryFormatter.js +0 -90
- package/lib/types/themeLibrary/themeLibraryType.js +0 -15
- package/lib/types/typeRepository.js +0 -46
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const path = require("path");
|
|
6
|
-
const terser = require("terser");
|
|
7
6
|
const {pd} = require("pretty-data");
|
|
8
7
|
const {parseJS, Syntax} = require("../utils/parseUtils");
|
|
9
|
-
|
|
8
|
+
const {encode: encodeMappings, decode: decodeMappings} = require("@jridgewell/sourcemap-codec");
|
|
10
9
|
|
|
11
10
|
const {isMethodCall} = require("../utils/ASTUtils");
|
|
12
11
|
const ModuleName = require("../utils/ModuleName");
|
|
@@ -19,7 +18,8 @@ const {SectionType} = require("./BundleDefinition");
|
|
|
19
18
|
const BundleWriter = require("./BundleWriter");
|
|
20
19
|
const log = require("@ui5/logger").getLogger("lbt:bundle:Builder");
|
|
21
20
|
|
|
22
|
-
const
|
|
21
|
+
const sourceMappingUrlPattern = /\/\/# sourceMappingURL=(.+)\s*$/;
|
|
22
|
+
const httpPattern = /^https?:\/\//i;
|
|
23
23
|
const xmlHtmlPrePattern = /<(?:\w+:)?pre\b/;
|
|
24
24
|
|
|
25
25
|
const strReplacements = {
|
|
@@ -40,61 +40,30 @@ function isEmptyBundle(resolvedBundle) {
|
|
|
40
40
|
return resolvedBundle.sections.every((section) => section.modules.length === 0);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const UI5BundleFormat = {
|
|
44
|
-
beforePreloads(outW, section) {
|
|
45
|
-
outW.write(`jQuery.sap.registerPreloadedModules(`);
|
|
46
|
-
outW.writeln(`{`);
|
|
47
|
-
if ( section.name ) {
|
|
48
|
-
outW.writeln(`"name":"${section.name}",`);
|
|
49
|
-
}
|
|
50
|
-
outW.writeln(`"version":"2.0",`);
|
|
51
|
-
outW.writeln(`"modules":{`);
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
afterPreloads(outW, section) {
|
|
55
|
-
outW.writeln(`}});`);
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
beforeBundleInfo(outW) {
|
|
59
|
-
outW.writeln("\"unsupported\"; /* 'bundleInfo' section mode not supported (requires ui5loader)");
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
afterBundleInfo(outW) {
|
|
63
|
-
outW.writeln("*/");
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
requireSync(outW, moduleName) {
|
|
67
|
-
outW.writeln(`sap.ui.requireSync("${ModuleName.toRequireJSName(moduleName)}");`);
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
shouldDecorate(resolvedModule) {
|
|
71
|
-
return resolvedModule.executes(UI5ClientConstants.MODULE__JQUERY_SAP_GLOBAL);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
|
|
75
43
|
const EVOBundleFormat = {
|
|
76
|
-
beforePreloads(
|
|
77
|
-
|
|
44
|
+
beforePreloads(section) {
|
|
45
|
+
return `sap.ui.require.preload({\n`;
|
|
78
46
|
},
|
|
79
47
|
|
|
80
|
-
afterPreloads(
|
|
81
|
-
|
|
48
|
+
afterPreloads(section) {
|
|
49
|
+
let str = `}`;
|
|
82
50
|
if ( section.name ) {
|
|
83
|
-
|
|
51
|
+
str += `,"${section.name}"`;
|
|
84
52
|
}
|
|
85
|
-
|
|
53
|
+
str += `);\n`;
|
|
54
|
+
return str;
|
|
86
55
|
},
|
|
87
56
|
|
|
88
|
-
beforeBundleInfo(
|
|
89
|
-
|
|
57
|
+
beforeBundleInfo() {
|
|
58
|
+
return "sap.ui.loader.config({bundlesUI5:{\n";
|
|
90
59
|
},
|
|
91
60
|
|
|
92
|
-
afterBundleInfo(
|
|
93
|
-
|
|
61
|
+
afterBundleInfo() {
|
|
62
|
+
return "}});\n";
|
|
94
63
|
},
|
|
95
64
|
|
|
96
|
-
requireSync(
|
|
97
|
-
|
|
65
|
+
requireSync(moduleName) {
|
|
66
|
+
return `sap.ui.requireSync("${ModuleName.toRequireJSName(moduleName)}");\n`;
|
|
98
67
|
},
|
|
99
68
|
|
|
100
69
|
shouldDecorate(resolvedModule) {
|
|
@@ -114,7 +83,6 @@ class BundleBuilder {
|
|
|
114
83
|
}
|
|
115
84
|
|
|
116
85
|
async createBundle(module, options) {
|
|
117
|
-
await this._prepare();
|
|
118
86
|
if ( options.numberOfParts > 1 ) {
|
|
119
87
|
const bundleInfos = [];
|
|
120
88
|
const submodules = await this.splitter.run( module, options );
|
|
@@ -127,17 +95,6 @@ class BundleBuilder {
|
|
|
127
95
|
}
|
|
128
96
|
}
|
|
129
97
|
|
|
130
|
-
_prepare() {
|
|
131
|
-
return Promise.all([
|
|
132
|
-
// check whether the resource pool contains debug and optimized sources
|
|
133
|
-
this.pool.findResource( ModuleName.getDebugName(UI5ClientConstants.MODULE__JQUERY_SAP_GLOBAL) ).
|
|
134
|
-
then( () => this.optimizedSources = true, () => this.optimizedSources = false ),
|
|
135
|
-
// check whether EVO modules are available. If so, use EVO APIs, else use old UI5 APIs.
|
|
136
|
-
this.pool.findResource(UI5ClientConstants.EVO_MARKER_RESOURCE).
|
|
137
|
-
then( () => this.targetBundleFormat = EVOBundleFormat, () => this.targetBundleFormat = UI5BundleFormat )
|
|
138
|
-
]);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
98
|
async _createBundle(module, options) {
|
|
142
99
|
const resolvedModule = await this.resolver.resolve(module);
|
|
143
100
|
if ( options.skipIfEmpty && isEmptyBundle(resolvedModule) ) {
|
|
@@ -148,15 +105,29 @@ class BundleBuilder {
|
|
|
148
105
|
|
|
149
106
|
this.options = options || {};
|
|
150
107
|
this.optimize = !!this.options.optimize;
|
|
108
|
+
if (this.options.sourceMap === undefined) {
|
|
109
|
+
this.options.sourceMap = true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Since UI5 Tooling 3.0: Always use modern API
|
|
113
|
+
this.targetBundleFormat = EVOBundleFormat;
|
|
151
114
|
|
|
152
|
-
// when decorateBootstrapModule is
|
|
153
|
-
// and don't write the try catch wrapper
|
|
115
|
+
// when decorateBootstrapModule is false,
|
|
116
|
+
// we don't write the optimized flag and don't write the try catch wrapper
|
|
154
117
|
this.shouldDecorate = this.options.decorateBootstrapModule &&
|
|
155
|
-
|
|
156
|
-
this.targetBundleFormat.shouldDecorate(resolvedModule));
|
|
118
|
+
this.targetBundleFormat.shouldDecorate(resolvedModule);
|
|
157
119
|
// TODO is the following condition ok or should the availability of jquery.sap.global.js be configurable?
|
|
158
120
|
this.jqglobalAvailable = !resolvedModule.containsGlobal;
|
|
159
121
|
this.openModule(resolvedModule.name);
|
|
122
|
+
|
|
123
|
+
this._sourceMap = {
|
|
124
|
+
version: 3,
|
|
125
|
+
file: path.posix.basename(resolvedModule.name),
|
|
126
|
+
sections: [],
|
|
127
|
+
};
|
|
128
|
+
this._bundleName = resolvedModule.name;
|
|
129
|
+
this._inlineContentCounter = 0;
|
|
130
|
+
|
|
160
131
|
let bundleInfos = [];
|
|
161
132
|
// create all sections in sequence
|
|
162
133
|
for ( const section of resolvedModule.sections ) {
|
|
@@ -185,6 +156,7 @@ class BundleBuilder {
|
|
|
185
156
|
return {
|
|
186
157
|
name: module.name,
|
|
187
158
|
content: this.outW.toString(),
|
|
159
|
+
sourceMap: this.options.sourceMap ? JSON.stringify(this._sourceMap) : null,
|
|
188
160
|
bundleInfo: bundleInfo
|
|
189
161
|
};
|
|
190
162
|
}
|
|
@@ -202,23 +174,36 @@ class BundleBuilder {
|
|
|
202
174
|
}
|
|
203
175
|
}
|
|
204
176
|
|
|
177
|
+
writeWithSourceMap(content) {
|
|
178
|
+
const transientSourceName =
|
|
179
|
+
`${path.posix.basename(this._bundleName)}?bundle-code-${this._inlineContentCounter++}`;
|
|
180
|
+
const sourceMap = createTransientSourceMap({
|
|
181
|
+
moduleName: transientSourceName,
|
|
182
|
+
moduleContent: content,
|
|
183
|
+
includeContent: true
|
|
184
|
+
});
|
|
185
|
+
this.addSourceMap(this._bundleName, sourceMap);
|
|
186
|
+
this.outW.write(content);
|
|
187
|
+
this.outW.ensureNewLine();
|
|
188
|
+
}
|
|
189
|
+
|
|
205
190
|
closeModule(resolvedModule) {
|
|
206
191
|
if ( resolvedModule.containsCore ) {
|
|
207
192
|
this.outW.ensureNewLine(); // for clarity and to avoid issues with single line comments
|
|
208
|
-
this.
|
|
209
|
-
|
|
193
|
+
this.writeWithSourceMap(
|
|
194
|
+
`// as this module contains the Core, we ensure that the Core has been booted\n` +
|
|
195
|
+
`sap.ui.getCore().boot && sap.ui.getCore().boot();`);
|
|
210
196
|
}
|
|
211
197
|
if ( this.shouldDecorate && this.options.addTryCatchRestartWrapper ) {
|
|
212
198
|
this.outW.ensureNewLine(); // for clarity and to avoid issues with single line comments
|
|
213
|
-
this.
|
|
214
|
-
|
|
215
|
-
|
|
199
|
+
this.writeWithSourceMap(
|
|
200
|
+
`} catch(oError) {\n` +
|
|
201
|
+
`if (oError.name != "Restart") { throw oError; }\n` +
|
|
202
|
+
`}`);
|
|
203
|
+
}
|
|
204
|
+
if (this.options.sourceMap) {
|
|
205
|
+
this.outW.writeln(`//# sourceMappingURL=${path.posix.basename(resolvedModule.name)}.map`);
|
|
216
206
|
}
|
|
217
|
-
/* NODE-TODO
|
|
218
|
-
if ( writeSourceMap && writeSourceMapAnnotation ) {
|
|
219
|
-
outW.ensureNewLine();
|
|
220
|
-
outW.write("//# sourceMappingURL=" + moduleName.getBaseName().replaceFirst("\\.js$", ".js.map"));
|
|
221
|
-
}*/
|
|
222
207
|
}
|
|
223
208
|
|
|
224
209
|
addSection(section) {
|
|
@@ -260,34 +245,43 @@ class BundleBuilder {
|
|
|
260
245
|
// TODO check that there are only JS modules contained
|
|
261
246
|
async writeRaw(section) {
|
|
262
247
|
// write all modules in sequence
|
|
263
|
-
for ( const
|
|
264
|
-
const resource = await this.pool.findResourceWithInfo(
|
|
248
|
+
for ( const moduleName of section.modules ) {
|
|
249
|
+
const resource = await this.pool.findResourceWithInfo(moduleName);
|
|
265
250
|
if ( resource != null ) {
|
|
266
|
-
this.outW.startSegment(
|
|
251
|
+
this.outW.startSegment(moduleName);
|
|
267
252
|
this.outW.ensureNewLine();
|
|
268
|
-
this.outW.writeln("//@ui5-bundle-raw-include " +
|
|
269
|
-
await this.writeRawModule(
|
|
253
|
+
this.outW.writeln("//@ui5-bundle-raw-include " + moduleName);
|
|
254
|
+
await this.writeRawModule(moduleName, resource);
|
|
270
255
|
const compressedSize = this.outW.endSegment();
|
|
271
|
-
log.verbose(" %s (%d,%d)",
|
|
256
|
+
log.verbose(" %s (%d,%d)", moduleName,
|
|
257
|
+
resource.info != null ? resource.info.size : -1, compressedSize);
|
|
272
258
|
if ( section.declareRawModules ) {
|
|
273
|
-
this.missingRawDeclarations.push(
|
|
259
|
+
this.missingRawDeclarations.push(moduleName);
|
|
274
260
|
}
|
|
275
|
-
if (
|
|
261
|
+
if ( moduleName === UI5ClientConstants.MODULE__JQUERY_SAP_GLOBAL ) {
|
|
276
262
|
this.jqglobalAvailable = true;
|
|
277
263
|
}
|
|
278
264
|
} else {
|
|
279
|
-
log.error(" couldn't find %s",
|
|
265
|
+
log.error(" couldn't find %s", moduleName);
|
|
280
266
|
}
|
|
281
267
|
}
|
|
282
268
|
}
|
|
283
269
|
|
|
284
|
-
async writeRawModule(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
270
|
+
async writeRawModule(moduleName, resource) {
|
|
271
|
+
this.outW.ensureNewLine();
|
|
272
|
+
let moduleContent = (await resource.buffer()).toString();
|
|
273
|
+
if (this.options.sourceMap) {
|
|
274
|
+
let moduleSourceMap;
|
|
275
|
+
({moduleContent, moduleSourceMap} =
|
|
276
|
+
await this.getSourceMapForModule({
|
|
277
|
+
moduleName,
|
|
278
|
+
moduleContent,
|
|
279
|
+
resourcePath: resource.getPath()
|
|
280
|
+
}));
|
|
281
|
+
this.addSourceMap(moduleName, moduleSourceMap);
|
|
288
282
|
}
|
|
283
|
+
this.outW.write(moduleContent);
|
|
289
284
|
this.outW.ensureNewLine();
|
|
290
|
-
this.outW.write( fileContent );
|
|
291
285
|
}
|
|
292
286
|
|
|
293
287
|
async writePreloadFunction(section) {
|
|
@@ -301,7 +295,7 @@ class BundleBuilder {
|
|
|
301
295
|
|
|
302
296
|
await this.rewriteAMDModules(sequence);
|
|
303
297
|
if ( sequence.length > 0 ) {
|
|
304
|
-
this.targetBundleFormat.beforePreloads(
|
|
298
|
+
this.writeWithSourceMap(this.targetBundleFormat.beforePreloads(section));
|
|
305
299
|
let i = 0;
|
|
306
300
|
for ( const module of sequence ) {
|
|
307
301
|
const resource = await this.pool.findResourceWithInfo(module);
|
|
@@ -309,7 +303,7 @@ class BundleBuilder {
|
|
|
309
303
|
if ( i>0 ) {
|
|
310
304
|
outW.writeln(",");
|
|
311
305
|
}
|
|
312
|
-
this.beforeWritePreloadModule(module, resource.info, resource);
|
|
306
|
+
// this.beforeWritePreloadModule(module, resource.info, resource);
|
|
313
307
|
outW.write(`\t"${module.toString()}":`);
|
|
314
308
|
outW.startSegment(module);
|
|
315
309
|
await this.writePreloadModule(module, resource.info, resource);
|
|
@@ -325,66 +319,83 @@ class BundleBuilder {
|
|
|
325
319
|
if ( i > 0 ) {
|
|
326
320
|
outW.writeln();
|
|
327
321
|
}
|
|
328
|
-
this.targetBundleFormat.afterPreloads(
|
|
322
|
+
outW.write(this.targetBundleFormat.afterPreloads(section));
|
|
329
323
|
}
|
|
330
324
|
|
|
331
325
|
// this.afterWriteFunctionPreloadSection();
|
|
332
326
|
}
|
|
333
327
|
|
|
334
|
-
async compressJS(fileContent, resource) {
|
|
335
|
-
if ( this.optimize ) {
|
|
336
|
-
const result = await terser.minify({
|
|
337
|
-
[resource.name]: String(fileContent)
|
|
338
|
-
}, {
|
|
339
|
-
compress: false, // TODO configure?
|
|
340
|
-
output: {
|
|
341
|
-
comments: copyrightCommentsPattern,
|
|
342
|
-
wrap_func_args: false
|
|
343
|
-
}
|
|
344
|
-
// , outFileName: resource.name
|
|
345
|
-
// , outSourceMap: true
|
|
346
|
-
});
|
|
347
|
-
// console.log(result.map);
|
|
348
|
-
// const map = new MOZ_SourceMap.SourceMapConsumer(result.map);
|
|
349
|
-
// map.eachMapping(function (m) { console.log(m); }); // console.log(map);
|
|
350
|
-
fileContent = result.code;
|
|
351
|
-
// throw new Error();
|
|
352
|
-
}
|
|
353
|
-
return fileContent;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
328
|
beforeWriteFunctionPreloadSection(sequence) {
|
|
357
329
|
// simple version: just sort alphabetically
|
|
358
330
|
sequence.sort();
|
|
359
331
|
}
|
|
360
332
|
|
|
333
|
+
addSourceMap(moduleName, map) {
|
|
334
|
+
if (!map) {
|
|
335
|
+
throw new Error("No source map provided");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (map.mappings.startsWith(";")) {
|
|
339
|
+
// If first line is not already mapped (typical for comments or parentheses), add a mapping to
|
|
340
|
+
// make sure that dev-tools (especially Chrome's) don't choose the end of the preceding module
|
|
341
|
+
// when the user tries to set a breakpoint from the bundle file
|
|
342
|
+
map.mappings = "AAAA" + map.mappings;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
map.sourceRoot = path.posix.relative(
|
|
346
|
+
path.posix.dirname(this._bundleName), path.posix.dirname(moduleName));
|
|
347
|
+
|
|
348
|
+
this._sourceMap.sections.push({
|
|
349
|
+
offset: {
|
|
350
|
+
line: this.outW.lineOffset,
|
|
351
|
+
column: this.outW.columnOffset
|
|
352
|
+
},
|
|
353
|
+
map
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
361
357
|
async rewriteAMDModules(sequence) {
|
|
362
358
|
if ( this.options.usePredefineCalls ) {
|
|
363
359
|
const outW = this.outW;
|
|
364
360
|
|
|
365
361
|
const remaining = [];
|
|
366
|
-
for ( const
|
|
367
|
-
if ( /\.js$/.test(
|
|
368
|
-
// console.log("Processing " +
|
|
369
|
-
const resource = await this.pool.findResourceWithInfo(
|
|
370
|
-
let
|
|
371
|
-
|
|
372
|
-
if (
|
|
373
|
-
|
|
362
|
+
for ( const moduleName of sequence ) {
|
|
363
|
+
if ( /\.js$/.test(moduleName) ) {
|
|
364
|
+
// console.log("Processing " + moduleName);
|
|
365
|
+
const resource = await this.pool.findResourceWithInfo(moduleName);
|
|
366
|
+
let moduleContent = (await resource.buffer()).toString();
|
|
367
|
+
let moduleSourceMap;
|
|
368
|
+
if (this.options.sourceMap) {
|
|
369
|
+
({moduleContent, moduleSourceMap} =
|
|
370
|
+
await this.getSourceMapForModule({
|
|
371
|
+
moduleName,
|
|
372
|
+
moduleContent,
|
|
373
|
+
resourcePath: resource.getPath()
|
|
374
|
+
}));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const rewriteRes = await rewriteDefine({
|
|
378
|
+
moduleName, moduleContent, moduleSourceMap
|
|
379
|
+
});
|
|
380
|
+
if (rewriteRes) {
|
|
381
|
+
const {moduleContent, moduleSourceMap} = rewriteRes;
|
|
382
|
+
outW.startSegment(moduleName);
|
|
374
383
|
outW.ensureNewLine();
|
|
375
|
-
|
|
376
|
-
|
|
384
|
+
if (moduleSourceMap) {
|
|
385
|
+
this.addSourceMap(moduleName, moduleSourceMap);
|
|
386
|
+
}
|
|
387
|
+
outW.write(moduleContent);
|
|
377
388
|
outW.ensureNewLine();
|
|
378
389
|
const compressedSize = outW.endSegment();
|
|
379
|
-
log.verbose(" %s (%d,%d)",
|
|
390
|
+
log.verbose(" %s (%d,%d)", moduleName,
|
|
380
391
|
resource.info != null ? resource.info.size : -1, compressedSize);
|
|
381
392
|
} else {
|
|
382
393
|
// keep unprocessed modules
|
|
383
|
-
remaining.push(
|
|
394
|
+
remaining.push(moduleName);
|
|
384
395
|
}
|
|
385
396
|
} else {
|
|
386
397
|
// keep unprocessed modules
|
|
387
|
-
remaining.push(
|
|
398
|
+
remaining.push(moduleName);
|
|
388
399
|
}
|
|
389
400
|
}
|
|
390
401
|
|
|
@@ -400,59 +411,71 @@ class BundleBuilder {
|
|
|
400
411
|
|
|
401
412
|
/**
|
|
402
413
|
*
|
|
403
|
-
* @param {string}
|
|
414
|
+
* @param {string} moduleName module name
|
|
404
415
|
* @param {ModuleInfo} info
|
|
405
416
|
* @param {module:@ui5/fs.Resource} resource
|
|
406
417
|
* @returns {Promise<boolean>}
|
|
407
418
|
*/
|
|
408
|
-
async writePreloadModule(
|
|
419
|
+
async writePreloadModule(moduleName, info, resource) {
|
|
409
420
|
const outW = this.outW;
|
|
410
421
|
|
|
411
|
-
if ( /\.js$/.test(
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
outW.
|
|
422
|
+
if ( /\.js$/.test(moduleName) && (info == null || !info.requiresTopLevelScope) ) {
|
|
423
|
+
outW.writeln(`function(){`);
|
|
424
|
+
// The module should be written to a new line in order for dev-tools to map breakpoints to it
|
|
425
|
+
outW.ensureNewLine();
|
|
426
|
+
let moduleContent = (await resource.buffer()).toString();
|
|
427
|
+
if (this.options.sourceMap) {
|
|
428
|
+
let moduleSourceMap;
|
|
429
|
+
({moduleContent, moduleSourceMap} =
|
|
430
|
+
await this.getSourceMapForModule({
|
|
431
|
+
moduleName,
|
|
432
|
+
moduleContent,
|
|
433
|
+
resourcePath: resource.getPath()
|
|
434
|
+
}));
|
|
435
|
+
|
|
436
|
+
this.addSourceMap(moduleName, moduleSourceMap);
|
|
437
|
+
}
|
|
438
|
+
outW.write(moduleContent);
|
|
415
439
|
this.exportGlobalNames(info);
|
|
416
440
|
outW.ensureNewLine();
|
|
417
441
|
outW.write(`}`);
|
|
418
|
-
} else if ( /\.js$/.test(
|
|
442
|
+
} else if ( /\.js$/.test(moduleName) /* implicitly: && info != null && info.requiresTopLevelScope */ ) {
|
|
419
443
|
log.warn("**** warning: module %s requires top level scope" +
|
|
420
|
-
" and can only be embedded as a string (requires 'eval')",
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const fileContent = await resource.buffer();
|
|
444
|
+
" and can only be embedded as a string (requires 'eval')", moduleName);
|
|
445
|
+
outW.write( makeStringLiteral( (await resource.buffer()).toString() ) );
|
|
446
|
+
} else if ( /\.html$/.test(moduleName) ) {
|
|
447
|
+
const fileContent = (await resource.buffer()).toString();
|
|
425
448
|
outW.write( makeStringLiteral( fileContent ) );
|
|
426
|
-
} else if ( /\.json$/.test(
|
|
427
|
-
let fileContent = await resource.buffer();
|
|
449
|
+
} else if ( /\.json$/.test(moduleName) ) {
|
|
450
|
+
let fileContent = (await resource.buffer()).toString();
|
|
428
451
|
if ( this.optimize ) {
|
|
429
452
|
try {
|
|
430
453
|
fileContent = JSON.stringify( JSON.parse( fileContent) );
|
|
431
454
|
} catch (e) {
|
|
432
|
-
log.verbose("Failed to parse JSON file %s. Ignoring error, skipping compression.",
|
|
455
|
+
log.verbose("Failed to parse JSON file %s. Ignoring error, skipping compression.", moduleName);
|
|
433
456
|
log.verbose(e);
|
|
434
457
|
}
|
|
435
458
|
}
|
|
436
459
|
outW.write(makeStringLiteral(fileContent));
|
|
437
|
-
} else if ( /\.xml$/.test(
|
|
438
|
-
let fileContent = await resource.buffer();
|
|
460
|
+
} else if ( /\.xml$/.test(moduleName) ) {
|
|
461
|
+
let fileContent = (await resource.buffer()).toString();
|
|
439
462
|
if ( this.optimize ) {
|
|
440
463
|
// For XML we use the pretty data
|
|
441
464
|
// Do not minify if XML(View) contains an <*:pre> tag,
|
|
442
465
|
// because whitespace of HTML <pre> should be preserved (should only happen rarely)
|
|
443
|
-
if (!xmlHtmlPrePattern.test(fileContent
|
|
444
|
-
fileContent = pd.xmlmin(fileContent
|
|
466
|
+
if (!xmlHtmlPrePattern.test(fileContent)) {
|
|
467
|
+
fileContent = pd.xmlmin(fileContent, false);
|
|
445
468
|
}
|
|
446
469
|
}
|
|
447
470
|
outW.write( makeStringLiteral( fileContent ) );
|
|
448
|
-
} else if ( /\.properties$/.test(
|
|
471
|
+
} else if ( /\.properties$/.test(moduleName) ) {
|
|
449
472
|
// Since the Builder is also used when building non-project resources (e.g. dependencies)
|
|
450
473
|
// *.properties files should be escaped if encoding option is specified
|
|
451
474
|
const fileContent = await escapePropertiesFile(resource);
|
|
452
475
|
|
|
453
476
|
outW.write( makeStringLiteral( fileContent ) );
|
|
454
477
|
} else {
|
|
455
|
-
log.error("don't know how to embed module " +
|
|
478
|
+
log.error("don't know how to embed module " + moduleName); // TODO throw?
|
|
456
479
|
}
|
|
457
480
|
|
|
458
481
|
return true;
|
|
@@ -470,18 +493,19 @@ class BundleBuilder {
|
|
|
470
493
|
this.outW.ensureNewLine();
|
|
471
494
|
info.exposedGlobals.forEach( (globalName) => {
|
|
472
495
|
// Note: globalName can be assumed to be a valid identifier as it is used as variable name anyhow
|
|
473
|
-
this.
|
|
496
|
+
this.writeWithSourceMap(`this.${globalName}=${globalName};\n`);
|
|
474
497
|
});
|
|
475
498
|
}
|
|
476
499
|
|
|
477
500
|
writeBundleInfos(sections) {
|
|
478
501
|
this.outW.ensureNewLine();
|
|
479
502
|
|
|
503
|
+
let bundleInfoStr = "";
|
|
480
504
|
if ( sections.length > 0 ) {
|
|
481
|
-
this.targetBundleFormat.beforeBundleInfo(
|
|
505
|
+
bundleInfoStr = this.targetBundleFormat.beforeBundleInfo();
|
|
482
506
|
sections.forEach((section, idx) => {
|
|
483
507
|
if ( idx > 0 ) {
|
|
484
|
-
|
|
508
|
+
bundleInfoStr += ",\n";
|
|
485
509
|
}
|
|
486
510
|
|
|
487
511
|
if (!section.name) {
|
|
@@ -492,31 +516,128 @@ class BundleBuilder {
|
|
|
492
516
|
`The info might not work as expected. ` +
|
|
493
517
|
`The name must match the bundle filename (incl. extension such as '.js')`);
|
|
494
518
|
}
|
|
495
|
-
|
|
519
|
+
bundleInfoStr += `"${section.name}":[${section.modules.map(makeStringLiteral).join(",")}]`;
|
|
496
520
|
});
|
|
497
|
-
|
|
498
|
-
this.targetBundleFormat.afterBundleInfo(
|
|
521
|
+
bundleInfoStr += "\n";
|
|
522
|
+
bundleInfoStr += this.targetBundleFormat.afterBundleInfo();
|
|
523
|
+
|
|
524
|
+
this.writeWithSourceMap(bundleInfoStr);
|
|
499
525
|
}
|
|
500
526
|
}
|
|
501
527
|
|
|
502
528
|
writeRequires(section) {
|
|
503
529
|
this.outW.ensureNewLine();
|
|
504
530
|
section.modules.forEach( (module) => {
|
|
505
|
-
this.targetBundleFormat.requireSync(
|
|
531
|
+
this.writeWithSourceMap(this.targetBundleFormat.requireSync(module));
|
|
506
532
|
});
|
|
507
533
|
}
|
|
534
|
+
|
|
535
|
+
async getSourceMapForModule({moduleName, moduleContent, resourcePath}) {
|
|
536
|
+
let moduleSourceMap = null;
|
|
537
|
+
let newModuleContent = moduleContent;
|
|
538
|
+
|
|
539
|
+
const sourceMapUrlMatch = moduleContent.match(sourceMappingUrlPattern);
|
|
540
|
+
if (sourceMapUrlMatch) {
|
|
541
|
+
const sourceMapUrl = sourceMapUrlMatch[1];
|
|
542
|
+
log.silly(`Found source map reference in content of module ${moduleName}: ${sourceMapUrl}`);
|
|
543
|
+
|
|
544
|
+
// Strip sourceMappingURL from module code to be bundled
|
|
545
|
+
// It has no effect and might be cause for confusion
|
|
546
|
+
newModuleContent = moduleContent.replace(sourceMappingUrlPattern, "");
|
|
547
|
+
|
|
548
|
+
if (sourceMapUrl) {
|
|
549
|
+
if (sourceMapUrl.startsWith("data:")) {
|
|
550
|
+
// Data-URI indicates an inline source map
|
|
551
|
+
const expectedTypeAndEncoding = "data:application/json;charset=utf-8;base64,";
|
|
552
|
+
if (sourceMapUrl.startsWith(expectedTypeAndEncoding)) {
|
|
553
|
+
const base64Content = sourceMapUrl.slice(expectedTypeAndEncoding.length);
|
|
554
|
+
moduleSourceMap = Buffer.from(base64Content, "base64").toString();
|
|
555
|
+
} else {
|
|
556
|
+
log.warn(
|
|
557
|
+
`Source map reference in module ${moduleName} is a data URI but has an unexpected` +
|
|
558
|
+
`encoding: ${sourceMapUrl}. Expected it to start with ` +
|
|
559
|
+
`"data:application/json;charset=utf-8;base64,"`);
|
|
560
|
+
}
|
|
561
|
+
} else if (httpPattern.test(sourceMapUrl)) {
|
|
562
|
+
log.warn(`Source map reference in module ${moduleName} is an absolute URL. ` +
|
|
563
|
+
`Currently, only relative URLs are supported.`);
|
|
564
|
+
} else if (path.posix.isAbsolute(sourceMapUrl)) {
|
|
565
|
+
log.warn(`Source map reference in module ${moduleName} is an absolute path. ` +
|
|
566
|
+
`Currently, only relative paths are supported.`);
|
|
567
|
+
} else {
|
|
568
|
+
const sourceMapPath = path.posix.join(path.posix.dirname(moduleName), sourceMapUrl);
|
|
569
|
+
|
|
570
|
+
try {
|
|
571
|
+
const sourceMapResource = await this.pool.findResource(sourceMapPath);
|
|
572
|
+
moduleSourceMap = (await sourceMapResource.buffer()).toString();
|
|
573
|
+
} catch (e) {
|
|
574
|
+
// No input source map
|
|
575
|
+
log.warn(`Unable to read source map for module ${moduleName}: ${e.message}`);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
} else {
|
|
580
|
+
const sourceMapFileCandidate = resourcePath.slice("/resources/".length) + ".map";
|
|
581
|
+
log.verbose(`Could not find a sourceMappingURL reference in content of module ${moduleName}. ` +
|
|
582
|
+
`Attempting to find a source map resource based on the module's path: ${sourceMapFileCandidate}`);
|
|
583
|
+
try {
|
|
584
|
+
const sourceMapResource = await this.pool.findResource(sourceMapFileCandidate);
|
|
585
|
+
if (sourceMapResource) {
|
|
586
|
+
moduleSourceMap = (await sourceMapResource.buffer()).toString();
|
|
587
|
+
}
|
|
588
|
+
} catch (e) {
|
|
589
|
+
// No input source map
|
|
590
|
+
log.verbose(`Could not find a source map for module ${moduleName}: ${e.message}`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
if (moduleSourceMap) {
|
|
596
|
+
moduleSourceMap = JSON.parse(moduleSourceMap);
|
|
597
|
+
|
|
598
|
+
// Check for index map, which is currently not supported
|
|
599
|
+
if (Array.isArray(moduleSourceMap.sections)) {
|
|
600
|
+
log.warn(
|
|
601
|
+
`Module ${moduleName} references an index source map which is currently not supported. ` +
|
|
602
|
+
`A transient source map will be created instead...`
|
|
603
|
+
);
|
|
604
|
+
moduleSourceMap = createTransientSourceMap({
|
|
605
|
+
moduleName: path.posix.basename(resourcePath),
|
|
606
|
+
moduleContent
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
} else {
|
|
610
|
+
log.verbose(`No source map available for module ${moduleName}. Creating transient source map...`);
|
|
611
|
+
moduleSourceMap = createTransientSourceMap({
|
|
612
|
+
moduleName: path.posix.basename(resourcePath),
|
|
613
|
+
moduleContent
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return {
|
|
618
|
+
moduleSourceMap,
|
|
619
|
+
moduleContent: newModuleContent
|
|
620
|
+
};
|
|
621
|
+
}
|
|
508
622
|
}
|
|
509
623
|
|
|
510
624
|
const CALL_SAP_UI_DEFINE = ["sap", "ui", "define"];
|
|
511
625
|
|
|
512
|
-
|
|
626
|
+
/*
|
|
627
|
+
* @param {object} parameters
|
|
628
|
+
* @param {string} parameters.moduleName
|
|
629
|
+
* @param {string} parameters.moduleContent
|
|
630
|
+
* @param {object} [parameters.moduleSourceMap]
|
|
631
|
+
* @returns {Promise<object|null>} Object containing <code>moduleContent</code> and
|
|
632
|
+
* <code>moduleSourceMap</code> (if one was provided) or <code>null</code> if no rewrite was applicable
|
|
633
|
+
*/
|
|
634
|
+
async function rewriteDefine({moduleName, moduleContent, moduleSourceMap}) {
|
|
513
635
|
let ast;
|
|
514
|
-
const codeStr = code.toString();
|
|
515
636
|
try {
|
|
516
|
-
ast = parseJS(
|
|
637
|
+
ast = parseJS(moduleContent, {range: true});
|
|
517
638
|
} catch (e) {
|
|
518
639
|
log.error("error while parsing %s: %s", moduleName, e.message);
|
|
519
|
-
return;
|
|
640
|
+
return {};
|
|
520
641
|
}
|
|
521
642
|
|
|
522
643
|
if ( ast.type === Syntax.Program &&
|
|
@@ -525,7 +646,6 @@ function rewriteDefine(targetBundleFormat, code, moduleName) {
|
|
|
525
646
|
const changes = [];
|
|
526
647
|
const defineCall = ast.body[0].expression;
|
|
527
648
|
|
|
528
|
-
|
|
529
649
|
// Inject module name if missing
|
|
530
650
|
if ( defineCall.arguments.length == 0 ||
|
|
531
651
|
defineCall.arguments[0].type !== Syntax.Literal ) {
|
|
@@ -543,7 +663,6 @@ function rewriteDefine(targetBundleFormat, code, moduleName) {
|
|
|
543
663
|
|
|
544
664
|
changes.push({
|
|
545
665
|
index,
|
|
546
|
-
count: 0,
|
|
547
666
|
value
|
|
548
667
|
});
|
|
549
668
|
}
|
|
@@ -555,29 +674,109 @@ function rewriteDefine(targetBundleFormat, code, moduleName) {
|
|
|
555
674
|
changes.push({
|
|
556
675
|
// asterisk marks the index: sap.ui.*define()
|
|
557
676
|
index: defineCall.callee.property.range[0],
|
|
558
|
-
count: 0,
|
|
559
677
|
value: "pre"
|
|
560
678
|
});
|
|
561
679
|
}
|
|
562
680
|
|
|
563
|
-
return
|
|
681
|
+
return transform(changes, moduleContent, moduleSourceMap);
|
|
564
682
|
}
|
|
565
683
|
|
|
566
|
-
return
|
|
684
|
+
return null;
|
|
567
685
|
}
|
|
568
686
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
687
|
+
/*
|
|
688
|
+
* @param {object[]} changes Changes that should be applied to the code
|
|
689
|
+
* @param {string} moduleContent Code to transform
|
|
690
|
+
* @param {object} [moduleSourceMap] Optional source map that should be aligned with the content change
|
|
691
|
+
* @returns {Promise<object>} Object containing <code>moduleContent</code> and
|
|
692
|
+
* <code>moduleSourceMap</code> (if one was provided)
|
|
693
|
+
*/
|
|
694
|
+
async function transform(changes, moduleContent, moduleSourceMap) {
|
|
695
|
+
const mappingChanges = [];
|
|
696
|
+
|
|
697
|
+
const array = Array.from(moduleContent);
|
|
698
|
+
// No sorting needed as changes are added in correct (reverse) order
|
|
573
699
|
changes.forEach((change) => {
|
|
700
|
+
if (moduleSourceMap) {
|
|
701
|
+
// Compute line and column for given index to re-align source map with inserted characters
|
|
702
|
+
const precedingCode = array.slice(0, change.index);
|
|
703
|
+
|
|
704
|
+
const line = precedingCode.reduce((lineCount, char) => {
|
|
705
|
+
if (char === "\n") {
|
|
706
|
+
lineCount++;
|
|
707
|
+
}
|
|
708
|
+
return lineCount;
|
|
709
|
+
}, 0);
|
|
710
|
+
const lineStartIndex = precedingCode.lastIndexOf("\n") + 1;
|
|
711
|
+
const column = change.index - lineStartIndex;
|
|
712
|
+
|
|
713
|
+
// Source map re-alignment needs to be done from front to back
|
|
714
|
+
mappingChanges.unshift({
|
|
715
|
+
line,
|
|
716
|
+
column,
|
|
717
|
+
columnDiff: change.value.length
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Apply modification
|
|
574
722
|
array.splice(
|
|
575
723
|
change.index,
|
|
576
|
-
|
|
724
|
+
0,
|
|
577
725
|
change.value
|
|
578
726
|
);
|
|
579
727
|
});
|
|
580
|
-
|
|
728
|
+
const transformedCode = array.join("");
|
|
729
|
+
|
|
730
|
+
if (moduleSourceMap) {
|
|
731
|
+
const mappings = decodeMappings(moduleSourceMap.mappings);
|
|
732
|
+
mappingChanges.forEach((mappingChange) => {
|
|
733
|
+
const lineMapping = mappings[mappingChange.line];
|
|
734
|
+
if (!lineMapping) {
|
|
735
|
+
// No mapping available that could be transformed
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
// Mapping structure:
|
|
739
|
+
// [generatedCodeColumn, sourceIndex, sourceCodeLine, sourceCodeColumn, nameIndex]
|
|
740
|
+
lineMapping.forEach((mapping) => {
|
|
741
|
+
if (mapping[0] > mappingChange.column) {
|
|
742
|
+
// All column mappings for the generated code after any change
|
|
743
|
+
// need to be moved by the amount of inserted characters
|
|
744
|
+
mapping[0] = mapping[0] + mappingChange.columnDiff;
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
moduleSourceMap.mappings = encodeMappings(mappings);
|
|
750
|
+
|
|
751
|
+
// No need for file information in source map since the bundled code does not exist in any file anyways
|
|
752
|
+
delete moduleSourceMap.file;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
return {
|
|
756
|
+
moduleContent: transformedCode,
|
|
757
|
+
moduleSourceMap
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
function createTransientSourceMap({moduleName, moduleContent, includeContent = false}) {
|
|
762
|
+
const sourceMap = {
|
|
763
|
+
version: 3,
|
|
764
|
+
sources: [moduleName],
|
|
765
|
+
// TODO: check whether moduleContent.match() with \n is better w.r.t performance/memory usage
|
|
766
|
+
mappings: encodeMappings(moduleContent.split("\n").map((line, i) => {
|
|
767
|
+
return [[0, 0, i, 0]];
|
|
768
|
+
}))
|
|
769
|
+
};
|
|
770
|
+
if (includeContent) {
|
|
771
|
+
sourceMap.sourcesContent = [moduleContent];
|
|
772
|
+
}
|
|
773
|
+
return sourceMap;
|
|
581
774
|
}
|
|
582
775
|
|
|
583
776
|
module.exports = BundleBuilder;
|
|
777
|
+
|
|
778
|
+
// Export local functions for testing only
|
|
779
|
+
/* istanbul ignore else */
|
|
780
|
+
if (process.env.NODE_ENV === "test") {
|
|
781
|
+
module.exports.__localFunctions__ = {rewriteDefine, createTransientSourceMap};
|
|
782
|
+
}
|