@ui5/builder 3.0.0-alpha.1 → 3.0.0-alpha.11
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 +867 -0
- package/index.js +0 -43
- package/lib/lbt/analyzer/FioriElementsAnalyzer.js +23 -12
- package/lib/lbt/analyzer/JSModuleAnalyzer.js +115 -61
- package/lib/lbt/analyzer/SmartTemplateAnalyzer.js +23 -12
- package/lib/lbt/analyzer/XMLCompositeAnalyzer.js +29 -19
- package/lib/lbt/analyzer/XMLTemplateAnalyzer.js +2 -1
- package/lib/lbt/analyzer/analyzeLibraryJS.js +15 -0
- package/lib/lbt/bundle/Builder.js +365 -138
- package/lib/lbt/bundle/BundleWriter.js +17 -0
- package/lib/lbt/bundle/Resolver.js +2 -2
- package/lib/lbt/calls/SapUiDefine.js +13 -7
- package/lib/lbt/resources/LocatorResource.js +7 -7
- package/lib/lbt/resources/LocatorResourcePool.js +8 -4
- package/lib/lbt/resources/ResourceCollector.js +22 -15
- package/lib/lbt/resources/ResourceInfoList.js +0 -1
- package/lib/lbt/resources/ResourcePool.js +7 -6
- package/lib/lbt/utils/ASTUtils.js +45 -18
- package/lib/lbt/utils/escapePropertiesFile.js +3 -6
- package/lib/lbt/utils/parseUtils.js +1 -1
- package/lib/processors/bundlers/moduleBundler.js +31 -10
- package/lib/processors/jsdoc/lib/createIndexFiles.js +1 -1
- package/lib/processors/jsdoc/lib/transformApiJson.js +78 -22
- package/lib/processors/jsdoc/lib/ui5/plugin.js +414 -122
- 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 +11 -5
- package/lib/tasks/buildThemes.js +1 -1
- package/lib/tasks/bundlers/generateBundle.js +70 -30
- package/lib/tasks/bundlers/generateComponentPreload.js +26 -19
- package/lib/tasks/bundlers/generateFlexChangesBundle.js +10 -5
- package/lib/tasks/bundlers/generateLibraryPreload.js +113 -94
- package/lib/tasks/bundlers/generateManifestBundle.js +8 -10
- package/lib/tasks/bundlers/generateStandaloneAppBundle.js +42 -10
- 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 +3 -3
- 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 +14 -4
- package/lib/tasks/taskRepository.js +1 -13
- package/lib/tasks/transformBootstrapHtml.js +6 -1
- package/package.json +13 -11
- package/lib/builder/BuildContext.js +0 -56
- package/lib/builder/ProjectBuildContext.js +0 -57
- package/lib/builder/builder.js +0 -419
- package/lib/tasks/TaskUtil.js +0 -160
- 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 -211
- package/lib/types/application/ApplicationFormatter.js +0 -227
- package/lib/types/application/applicationType.js +0 -15
- package/lib/types/library/LibraryBuilder.js +0 -231
- 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 -63
- package/lib/types/themeLibrary/ThemeLibraryFormatter.js +0 -90
- package/lib/types/themeLibrary/themeLibraryType.js +0 -15
- package/lib/types/typeRepository.js +0 -46
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const {pd} = require("pretty-data");
|
|
7
7
|
const {parseJS, Syntax} = require("../utils/parseUtils");
|
|
8
|
-
|
|
8
|
+
const {encode: encodeMappings, decode: decodeMappings} = require("@jridgewell/sourcemap-codec");
|
|
9
9
|
|
|
10
10
|
const {isMethodCall} = require("../utils/ASTUtils");
|
|
11
11
|
const ModuleName = require("../utils/ModuleName");
|
|
@@ -18,6 +18,8 @@ const {SectionType} = require("./BundleDefinition");
|
|
|
18
18
|
const BundleWriter = require("./BundleWriter");
|
|
19
19
|
const log = require("@ui5/logger").getLogger("lbt:bundle:Builder");
|
|
20
20
|
|
|
21
|
+
const sourceMappingUrlPattern = /\/\/# sourceMappingURL=(.+)\s*$/;
|
|
22
|
+
const httpPattern = /^https?:\/\//i;
|
|
21
23
|
const xmlHtmlPrePattern = /<(?:\w+:)?pre\b/;
|
|
22
24
|
|
|
23
25
|
const strReplacements = {
|
|
@@ -38,61 +40,30 @@ function isEmptyBundle(resolvedBundle) {
|
|
|
38
40
|
return resolvedBundle.sections.every((section) => section.modules.length === 0);
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
const UI5BundleFormat = {
|
|
42
|
-
beforePreloads(outW, section) {
|
|
43
|
-
outW.write(`jQuery.sap.registerPreloadedModules(`);
|
|
44
|
-
outW.writeln(`{`);
|
|
45
|
-
if ( section.name ) {
|
|
46
|
-
outW.writeln(`"name":"${section.name}",`);
|
|
47
|
-
}
|
|
48
|
-
outW.writeln(`"version":"2.0",`);
|
|
49
|
-
outW.writeln(`"modules":{`);
|
|
50
|
-
},
|
|
51
|
-
|
|
52
|
-
afterPreloads(outW, section) {
|
|
53
|
-
outW.writeln(`}});`);
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
beforeBundleInfo(outW) {
|
|
57
|
-
outW.writeln("\"unsupported\"; /* 'bundleInfo' section mode not supported (requires ui5loader)");
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
afterBundleInfo(outW) {
|
|
61
|
-
outW.writeln("*/");
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
requireSync(outW, moduleName) {
|
|
65
|
-
outW.writeln(`sap.ui.requireSync("${ModuleName.toRequireJSName(moduleName)}");`);
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
shouldDecorate(resolvedModule) {
|
|
69
|
-
return resolvedModule.executes(UI5ClientConstants.MODULE__JQUERY_SAP_GLOBAL);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
43
|
const EVOBundleFormat = {
|
|
74
|
-
beforePreloads(
|
|
75
|
-
|
|
44
|
+
beforePreloads(section) {
|
|
45
|
+
return `sap.ui.require.preload({\n`;
|
|
76
46
|
},
|
|
77
47
|
|
|
78
|
-
afterPreloads(
|
|
79
|
-
|
|
48
|
+
afterPreloads(section) {
|
|
49
|
+
let str = `}`;
|
|
80
50
|
if ( section.name ) {
|
|
81
|
-
|
|
51
|
+
str += `,"${section.name}"`;
|
|
82
52
|
}
|
|
83
|
-
|
|
53
|
+
str += `);\n`;
|
|
54
|
+
return str;
|
|
84
55
|
},
|
|
85
56
|
|
|
86
|
-
beforeBundleInfo(
|
|
87
|
-
|
|
57
|
+
beforeBundleInfo() {
|
|
58
|
+
return "sap.ui.loader.config({bundlesUI5:{\n";
|
|
88
59
|
},
|
|
89
60
|
|
|
90
|
-
afterBundleInfo(
|
|
91
|
-
|
|
61
|
+
afterBundleInfo() {
|
|
62
|
+
return "}});\n";
|
|
92
63
|
},
|
|
93
64
|
|
|
94
|
-
requireSync(
|
|
95
|
-
|
|
65
|
+
requireSync(moduleName) {
|
|
66
|
+
return `sap.ui.requireSync("${ModuleName.toRequireJSName(moduleName)}");\n`;
|
|
96
67
|
},
|
|
97
68
|
|
|
98
69
|
shouldDecorate(resolvedModule) {
|
|
@@ -112,7 +83,6 @@ class BundleBuilder {
|
|
|
112
83
|
}
|
|
113
84
|
|
|
114
85
|
async createBundle(module, options) {
|
|
115
|
-
await this._prepare();
|
|
116
86
|
if ( options.numberOfParts > 1 ) {
|
|
117
87
|
const bundleInfos = [];
|
|
118
88
|
const submodules = await this.splitter.run( module, options );
|
|
@@ -125,17 +95,6 @@ class BundleBuilder {
|
|
|
125
95
|
}
|
|
126
96
|
}
|
|
127
97
|
|
|
128
|
-
_prepare() {
|
|
129
|
-
return Promise.all([
|
|
130
|
-
// check whether the resource pool contains debug and optimized sources
|
|
131
|
-
this.pool.findResource( ModuleName.getDebugName(UI5ClientConstants.MODULE__JQUERY_SAP_GLOBAL) ).
|
|
132
|
-
then( () => this.optimizedSources = true, () => this.optimizedSources = false ),
|
|
133
|
-
// check whether EVO modules are available. If so, use EVO APIs, else use old UI5 APIs.
|
|
134
|
-
this.pool.findResource(UI5ClientConstants.EVO_MARKER_RESOURCE).
|
|
135
|
-
then( () => this.targetBundleFormat = EVOBundleFormat, () => this.targetBundleFormat = UI5BundleFormat )
|
|
136
|
-
]);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
98
|
async _createBundle(module, options) {
|
|
140
99
|
const resolvedModule = await this.resolver.resolve(module);
|
|
141
100
|
if ( options.skipIfEmpty && isEmptyBundle(resolvedModule) ) {
|
|
@@ -146,15 +105,29 @@ class BundleBuilder {
|
|
|
146
105
|
|
|
147
106
|
this.options = options || {};
|
|
148
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;
|
|
149
114
|
|
|
150
|
-
// when decorateBootstrapModule is
|
|
151
|
-
// 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
|
|
152
117
|
this.shouldDecorate = this.options.decorateBootstrapModule &&
|
|
153
|
-
|
|
154
|
-
this.targetBundleFormat.shouldDecorate(resolvedModule));
|
|
118
|
+
this.targetBundleFormat.shouldDecorate(resolvedModule);
|
|
155
119
|
// TODO is the following condition ok or should the availability of jquery.sap.global.js be configurable?
|
|
156
120
|
this.jqglobalAvailable = !resolvedModule.containsGlobal;
|
|
157
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
|
+
|
|
158
131
|
let bundleInfos = [];
|
|
159
132
|
// create all sections in sequence
|
|
160
133
|
for ( const section of resolvedModule.sections ) {
|
|
@@ -183,6 +156,7 @@ class BundleBuilder {
|
|
|
183
156
|
return {
|
|
184
157
|
name: module.name,
|
|
185
158
|
content: this.outW.toString(),
|
|
159
|
+
sourceMap: this.options.sourceMap ? JSON.stringify(this._sourceMap) : null,
|
|
186
160
|
bundleInfo: bundleInfo
|
|
187
161
|
};
|
|
188
162
|
}
|
|
@@ -200,23 +174,36 @@ class BundleBuilder {
|
|
|
200
174
|
}
|
|
201
175
|
}
|
|
202
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
|
+
|
|
203
190
|
closeModule(resolvedModule) {
|
|
204
191
|
if ( resolvedModule.containsCore ) {
|
|
205
192
|
this.outW.ensureNewLine(); // for clarity and to avoid issues with single line comments
|
|
206
|
-
this.
|
|
207
|
-
|
|
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();`);
|
|
208
196
|
}
|
|
209
197
|
if ( this.shouldDecorate && this.options.addTryCatchRestartWrapper ) {
|
|
210
198
|
this.outW.ensureNewLine(); // for clarity and to avoid issues with single line comments
|
|
211
|
-
this.
|
|
212
|
-
|
|
213
|
-
|
|
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`);
|
|
214
206
|
}
|
|
215
|
-
/* NODE-TODO
|
|
216
|
-
if ( writeSourceMap && writeSourceMapAnnotation ) {
|
|
217
|
-
outW.ensureNewLine();
|
|
218
|
-
outW.write("//# sourceMappingURL=" + moduleName.getBaseName().replaceFirst("\\.js$", ".js.map"));
|
|
219
|
-
}*/
|
|
220
207
|
}
|
|
221
208
|
|
|
222
209
|
addSection(section) {
|
|
@@ -258,31 +245,43 @@ class BundleBuilder {
|
|
|
258
245
|
// TODO check that there are only JS modules contained
|
|
259
246
|
async writeRaw(section) {
|
|
260
247
|
// write all modules in sequence
|
|
261
|
-
for ( const
|
|
262
|
-
const resource = await this.pool.findResourceWithInfo(
|
|
248
|
+
for ( const moduleName of section.modules ) {
|
|
249
|
+
const resource = await this.pool.findResourceWithInfo(moduleName);
|
|
263
250
|
if ( resource != null ) {
|
|
264
|
-
this.outW.startSegment(
|
|
251
|
+
this.outW.startSegment(moduleName);
|
|
265
252
|
this.outW.ensureNewLine();
|
|
266
|
-
this.outW.writeln("//@ui5-bundle-raw-include " +
|
|
267
|
-
await this.writeRawModule(
|
|
253
|
+
this.outW.writeln("//@ui5-bundle-raw-include " + moduleName);
|
|
254
|
+
await this.writeRawModule(moduleName, resource);
|
|
268
255
|
const compressedSize = this.outW.endSegment();
|
|
269
|
-
log.verbose(" %s (%d,%d)",
|
|
256
|
+
log.verbose(" %s (%d,%d)", moduleName,
|
|
257
|
+
resource.info != null ? resource.info.size : -1, compressedSize);
|
|
270
258
|
if ( section.declareRawModules ) {
|
|
271
|
-
this.missingRawDeclarations.push(
|
|
259
|
+
this.missingRawDeclarations.push(moduleName);
|
|
272
260
|
}
|
|
273
|
-
if (
|
|
261
|
+
if ( moduleName === UI5ClientConstants.MODULE__JQUERY_SAP_GLOBAL ) {
|
|
274
262
|
this.jqglobalAvailable = true;
|
|
275
263
|
}
|
|
276
264
|
} else {
|
|
277
|
-
log.error(" couldn't find %s",
|
|
265
|
+
log.error(" couldn't find %s", moduleName);
|
|
278
266
|
}
|
|
279
267
|
}
|
|
280
268
|
}
|
|
281
269
|
|
|
282
|
-
async writeRawModule(
|
|
283
|
-
|
|
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);
|
|
282
|
+
}
|
|
283
|
+
this.outW.write(moduleContent);
|
|
284
284
|
this.outW.ensureNewLine();
|
|
285
|
-
this.outW.write(fileContent);
|
|
286
285
|
}
|
|
287
286
|
|
|
288
287
|
async writePreloadFunction(section) {
|
|
@@ -296,7 +295,7 @@ class BundleBuilder {
|
|
|
296
295
|
|
|
297
296
|
await this.rewriteAMDModules(sequence);
|
|
298
297
|
if ( sequence.length > 0 ) {
|
|
299
|
-
this.targetBundleFormat.beforePreloads(
|
|
298
|
+
this.writeWithSourceMap(this.targetBundleFormat.beforePreloads(section));
|
|
300
299
|
let i = 0;
|
|
301
300
|
for ( const module of sequence ) {
|
|
302
301
|
const resource = await this.pool.findResourceWithInfo(module);
|
|
@@ -304,7 +303,7 @@ class BundleBuilder {
|
|
|
304
303
|
if ( i>0 ) {
|
|
305
304
|
outW.writeln(",");
|
|
306
305
|
}
|
|
307
|
-
this.beforeWritePreloadModule(module, resource.info, resource);
|
|
306
|
+
// this.beforeWritePreloadModule(module, resource.info, resource);
|
|
308
307
|
outW.write(`\t"${module.toString()}":`);
|
|
309
308
|
outW.startSegment(module);
|
|
310
309
|
await this.writePreloadModule(module, resource.info, resource);
|
|
@@ -320,7 +319,7 @@ class BundleBuilder {
|
|
|
320
319
|
if ( i > 0 ) {
|
|
321
320
|
outW.writeln();
|
|
322
321
|
}
|
|
323
|
-
this.targetBundleFormat.afterPreloads(
|
|
322
|
+
outW.write(this.targetBundleFormat.afterPreloads(section));
|
|
324
323
|
}
|
|
325
324
|
|
|
326
325
|
// this.afterWriteFunctionPreloadSection();
|
|
@@ -331,32 +330,72 @@ class BundleBuilder {
|
|
|
331
330
|
sequence.sort();
|
|
332
331
|
}
|
|
333
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
|
+
|
|
334
357
|
async rewriteAMDModules(sequence) {
|
|
335
358
|
if ( this.options.usePredefineCalls ) {
|
|
336
359
|
const outW = this.outW;
|
|
337
360
|
|
|
338
361
|
const remaining = [];
|
|
339
|
-
for ( const
|
|
340
|
-
if ( /\.js$/.test(
|
|
341
|
-
// console.log("Processing " +
|
|
342
|
-
const resource = await this.pool.findResourceWithInfo(
|
|
343
|
-
let moduleContent = await resource.
|
|
344
|
-
|
|
345
|
-
if (
|
|
346
|
-
|
|
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);
|
|
347
383
|
outW.ensureNewLine();
|
|
384
|
+
if (moduleSourceMap) {
|
|
385
|
+
this.addSourceMap(moduleName, moduleSourceMap);
|
|
386
|
+
}
|
|
348
387
|
outW.write(moduleContent);
|
|
349
388
|
outW.ensureNewLine();
|
|
350
389
|
const compressedSize = outW.endSegment();
|
|
351
|
-
log.verbose(" %s (%d,%d)",
|
|
390
|
+
log.verbose(" %s (%d,%d)", moduleName,
|
|
352
391
|
resource.info != null ? resource.info.size : -1, compressedSize);
|
|
353
392
|
} else {
|
|
354
393
|
// keep unprocessed modules
|
|
355
|
-
remaining.push(
|
|
394
|
+
remaining.push(moduleName);
|
|
356
395
|
}
|
|
357
396
|
} else {
|
|
358
397
|
// keep unprocessed modules
|
|
359
|
-
remaining.push(
|
|
398
|
+
remaining.push(moduleName);
|
|
360
399
|
}
|
|
361
400
|
}
|
|
362
401
|
|
|
@@ -372,42 +411,54 @@ class BundleBuilder {
|
|
|
372
411
|
|
|
373
412
|
/**
|
|
374
413
|
*
|
|
375
|
-
* @param {string}
|
|
414
|
+
* @param {string} moduleName module name
|
|
376
415
|
* @param {ModuleInfo} info
|
|
377
416
|
* @param {module:@ui5/fs.Resource} resource
|
|
378
417
|
* @returns {Promise<boolean>}
|
|
379
418
|
*/
|
|
380
|
-
async writePreloadModule(
|
|
419
|
+
async writePreloadModule(moduleName, info, resource) {
|
|
381
420
|
const outW = this.outW;
|
|
382
421
|
|
|
383
|
-
if ( /\.js$/.test(
|
|
384
|
-
|
|
385
|
-
|
|
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
|
+
}
|
|
386
438
|
outW.write(moduleContent);
|
|
387
439
|
this.exportGlobalNames(info);
|
|
388
440
|
outW.ensureNewLine();
|
|
389
441
|
outW.write(`}`);
|
|
390
|
-
} else if ( /\.js$/.test(
|
|
442
|
+
} else if ( /\.js$/.test(moduleName) /* implicitly: && info != null && info.requiresTopLevelScope */ ) {
|
|
391
443
|
log.warn("**** warning: module %s requires top level scope" +
|
|
392
|
-
" and can only be embedded as a string (requires 'eval')",
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
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();
|
|
397
448
|
outW.write( makeStringLiteral( fileContent ) );
|
|
398
|
-
} else if ( /\.json$/.test(
|
|
399
|
-
let fileContent = await resource.buffer();
|
|
449
|
+
} else if ( /\.json$/.test(moduleName) ) {
|
|
450
|
+
let fileContent = (await resource.buffer()).toString();
|
|
400
451
|
if ( this.optimize ) {
|
|
401
452
|
try {
|
|
402
453
|
fileContent = JSON.stringify( JSON.parse( fileContent) );
|
|
403
454
|
} catch (e) {
|
|
404
|
-
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);
|
|
405
456
|
log.verbose(e);
|
|
406
457
|
}
|
|
407
458
|
}
|
|
408
459
|
outW.write(makeStringLiteral(fileContent));
|
|
409
|
-
} else if ( /\.xml$/.test(
|
|
410
|
-
let fileContent = await resource.
|
|
460
|
+
} else if ( /\.xml$/.test(moduleName) ) {
|
|
461
|
+
let fileContent = (await resource.buffer()).toString();
|
|
411
462
|
if ( this.optimize ) {
|
|
412
463
|
// For XML we use the pretty data
|
|
413
464
|
// Do not minify if XML(View) contains an <*:pre> tag,
|
|
@@ -417,14 +468,14 @@ class BundleBuilder {
|
|
|
417
468
|
}
|
|
418
469
|
}
|
|
419
470
|
outW.write( makeStringLiteral( fileContent ) );
|
|
420
|
-
} else if ( /\.properties$/.test(
|
|
471
|
+
} else if ( /\.properties$/.test(moduleName) ) {
|
|
421
472
|
// Since the Builder is also used when building non-project resources (e.g. dependencies)
|
|
422
473
|
// *.properties files should be escaped if encoding option is specified
|
|
423
474
|
const fileContent = await escapePropertiesFile(resource);
|
|
424
475
|
|
|
425
476
|
outW.write( makeStringLiteral( fileContent ) );
|
|
426
477
|
} else {
|
|
427
|
-
log.error("don't know how to embed module " +
|
|
478
|
+
log.error("don't know how to embed module " + moduleName); // TODO throw?
|
|
428
479
|
}
|
|
429
480
|
|
|
430
481
|
return true;
|
|
@@ -442,18 +493,19 @@ class BundleBuilder {
|
|
|
442
493
|
this.outW.ensureNewLine();
|
|
443
494
|
info.exposedGlobals.forEach( (globalName) => {
|
|
444
495
|
// Note: globalName can be assumed to be a valid identifier as it is used as variable name anyhow
|
|
445
|
-
this.
|
|
496
|
+
this.writeWithSourceMap(`this.${globalName}=${globalName};\n`);
|
|
446
497
|
});
|
|
447
498
|
}
|
|
448
499
|
|
|
449
500
|
writeBundleInfos(sections) {
|
|
450
501
|
this.outW.ensureNewLine();
|
|
451
502
|
|
|
503
|
+
let bundleInfoStr = "";
|
|
452
504
|
if ( sections.length > 0 ) {
|
|
453
|
-
this.targetBundleFormat.beforeBundleInfo(
|
|
505
|
+
bundleInfoStr = this.targetBundleFormat.beforeBundleInfo();
|
|
454
506
|
sections.forEach((section, idx) => {
|
|
455
507
|
if ( idx > 0 ) {
|
|
456
|
-
|
|
508
|
+
bundleInfoStr += ",\n";
|
|
457
509
|
}
|
|
458
510
|
|
|
459
511
|
if (!section.name) {
|
|
@@ -464,31 +516,128 @@ class BundleBuilder {
|
|
|
464
516
|
`The info might not work as expected. ` +
|
|
465
517
|
`The name must match the bundle filename (incl. extension such as '.js')`);
|
|
466
518
|
}
|
|
467
|
-
|
|
519
|
+
bundleInfoStr += `"${section.name}":[${section.modules.map(makeStringLiteral).join(",")}]`;
|
|
468
520
|
});
|
|
469
|
-
|
|
470
|
-
this.targetBundleFormat.afterBundleInfo(
|
|
521
|
+
bundleInfoStr += "\n";
|
|
522
|
+
bundleInfoStr += this.targetBundleFormat.afterBundleInfo();
|
|
523
|
+
|
|
524
|
+
this.writeWithSourceMap(bundleInfoStr);
|
|
471
525
|
}
|
|
472
526
|
}
|
|
473
527
|
|
|
474
528
|
writeRequires(section) {
|
|
475
529
|
this.outW.ensureNewLine();
|
|
476
530
|
section.modules.forEach( (module) => {
|
|
477
|
-
this.targetBundleFormat.requireSync(
|
|
531
|
+
this.writeWithSourceMap(this.targetBundleFormat.requireSync(module));
|
|
478
532
|
});
|
|
479
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
|
+
}
|
|
480
622
|
}
|
|
481
623
|
|
|
482
624
|
const CALL_SAP_UI_DEFINE = ["sap", "ui", "define"];
|
|
483
625
|
|
|
484
|
-
|
|
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}) {
|
|
485
635
|
let ast;
|
|
486
|
-
const codeStr = code.toString();
|
|
487
636
|
try {
|
|
488
|
-
ast = parseJS(
|
|
637
|
+
ast = parseJS(moduleContent, {range: true});
|
|
489
638
|
} catch (e) {
|
|
490
639
|
log.error("error while parsing %s: %s", moduleName, e.message);
|
|
491
|
-
return;
|
|
640
|
+
return {};
|
|
492
641
|
}
|
|
493
642
|
|
|
494
643
|
if ( ast.type === Syntax.Program &&
|
|
@@ -497,10 +646,9 @@ function rewriteDefine(targetBundleFormat, code, moduleName) {
|
|
|
497
646
|
const changes = [];
|
|
498
647
|
const defineCall = ast.body[0].expression;
|
|
499
648
|
|
|
500
|
-
|
|
501
649
|
// Inject module name if missing
|
|
502
650
|
if ( defineCall.arguments.length == 0 ||
|
|
503
|
-
defineCall.arguments[0].type
|
|
651
|
+
![Syntax.Literal, Syntax.TemplateLiteral].includes(defineCall.arguments[0].type)) {
|
|
504
652
|
let value = `"${ModuleName.toRequireJSName(moduleName)}"`;
|
|
505
653
|
let index;
|
|
506
654
|
|
|
@@ -515,7 +663,6 @@ function rewriteDefine(targetBundleFormat, code, moduleName) {
|
|
|
515
663
|
|
|
516
664
|
changes.push({
|
|
517
665
|
index,
|
|
518
|
-
count: 0,
|
|
519
666
|
value
|
|
520
667
|
});
|
|
521
668
|
}
|
|
@@ -527,29 +674,109 @@ function rewriteDefine(targetBundleFormat, code, moduleName) {
|
|
|
527
674
|
changes.push({
|
|
528
675
|
// asterisk marks the index: sap.ui.*define()
|
|
529
676
|
index: defineCall.callee.property.range[0],
|
|
530
|
-
count: 0,
|
|
531
677
|
value: "pre"
|
|
532
678
|
});
|
|
533
679
|
}
|
|
534
680
|
|
|
535
|
-
return
|
|
681
|
+
return transform(changes, moduleContent, moduleSourceMap);
|
|
536
682
|
}
|
|
537
683
|
|
|
538
|
-
return
|
|
684
|
+
return null;
|
|
539
685
|
}
|
|
540
686
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
|
545
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
|
|
546
722
|
array.splice(
|
|
547
723
|
change.index,
|
|
548
|
-
|
|
724
|
+
0,
|
|
549
725
|
change.value
|
|
550
726
|
);
|
|
551
727
|
});
|
|
552
|
-
|
|
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;
|
|
553
774
|
}
|
|
554
775
|
|
|
555
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
|
+
}
|