Skip to content

Commit e3e8f85

Browse files
d3xter666KlattG
andauthored
[FEATURE] Apply specVersion defaults from ui5.yaml.json schema (#733)
JIRA: CPOUI5FOUNDATION-835 --------- Co-authored-by: Günter Klatt <[email protected]>
1 parent e06c62a commit e3e8f85

File tree

5 files changed

+160
-41
lines changed

5 files changed

+160
-41
lines changed

lib/build/definitions/application.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {enhancePatternWithExcludes} from "./_utils.js";
2+
import {enhanceBundlesWithDefaults} from "../../validation/validator.js";
23

34
/**
45
* Get tasks and their configuration for a given application project
@@ -84,7 +85,10 @@ export default function({project, taskUtil, getTask}) {
8485
requiresDependencies: true,
8586
taskFunction: async ({workspace, dependencies, taskUtil, options}) => {
8687
const generateBundleTask = await getTask("generateBundle");
87-
return bundles.reduce(function(sequence, bundle) {
88+
// Async resolve default values for bundle definitions and options
89+
const bundlesDefaults = await enhanceBundlesWithDefaults(bundles, taskUtil.getProject());
90+
91+
return bundlesDefaults.reduce(async function(sequence, bundle) {
8892
return sequence.then(function() {
8993
return generateBundleTask.task({
9094
workspace,

lib/build/definitions/library.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {enhancePatternWithExcludes} from "./_utils.js";
2+
import {enhanceBundlesWithDefaults} from "../../validation/validator.js";
23

34
/**
45
* Get tasks and their configuration for a given application project
@@ -119,7 +120,10 @@ export default function({project, taskUtil, getTask}) {
119120
requiresDependencies: true,
120121
taskFunction: async ({workspace, dependencies, taskUtil, options}) => {
121122
const generateBundleTask = await getTask("generateBundle");
122-
return bundles.reduce(function(sequence, bundle) {
123+
// Async resolve default values for bundle definitions and options
124+
const bundlesDefaults = await enhanceBundlesWithDefaults(bundles, taskUtil.getProject());
125+
126+
return bundlesDefaults.reduce(function(sequence, bundle) {
123127
return sequence.then(function() {
124128
return generateBundleTask.task({
125129
workspace,

lib/validation/validator.js

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const SCHEMA_VARIANTS = {
1818
};
1919

2020
class Validator {
21-
constructor({Ajv, ajvErrors, schemaName}) {
21+
constructor({Ajv, ajvErrors, schemaName, ajvConfig}) {
2222
if (!schemaName || !SCHEMA_VARIANTS[schemaName]) {
2323
throw new Error(
2424
`"schemaName" is missing or incorrect. The available schemaName variants are ${Object.keys(
@@ -29,11 +29,12 @@ class Validator {
2929

3030
this._schemaName = SCHEMA_VARIANTS[schemaName];
3131

32-
this.ajv = new Ajv({
32+
ajvConfig = Object.assign({
3333
allErrors: true,
3434
jsonPointers: true,
3535
loadSchema: Validator.loadSchema
36-
});
36+
}, ajvConfig);
37+
this.ajv = new Ajv(ajvConfig);
3738
ajvErrors(this.ajv);
3839
}
3940

@@ -77,6 +78,7 @@ class Validator {
7778
}
7879

7980
const validator = Object.create(null);
81+
const defaultsValidator = Object.create(null);
8082

8183
async function _validate(schemaName, options) {
8284
if (!validator[schemaName]) {
@@ -91,6 +93,27 @@ async function _validate(schemaName, options) {
9193
await schemaValidator.validate(options);
9294
}
9395

96+
async function _validateAndSetDefaults(schemaName, options) {
97+
if (!defaultsValidator[schemaName]) {
98+
defaultsValidator[schemaName] = (async () => {
99+
const {default: Ajv} = await import("ajv");
100+
const {default: ajvErrors} = await import("ajv-errors");
101+
return new Validator({Ajv, ajvErrors, ajvConfig: {useDefaults: true}, schemaName});
102+
})();
103+
}
104+
105+
// When AJV is configured with useDefaults: true, it may add properties to the
106+
// provided configuration that were not initially present. This behavior can
107+
// lead to unexpected side effects and potential issues. To avoid these
108+
// problems, we create a copy of the configuration. If we need the altered
109+
// configuration later, we return this copied version.
110+
const optionsCopy = structuredClone(options);
111+
const schemaValidator = await defaultsValidator[schemaName];
112+
await schemaValidator.validate(optionsCopy);
113+
114+
return optionsCopy;
115+
}
116+
94117
/**
95118
* Validates the given ui5 configuration.
96119
*
@@ -114,6 +137,52 @@ export async function validate(options) {
114137
await _validate("ui5", options);
115138
}
116139

140+
/**
141+
* Validates the given ui5 configuration and returns default values if none are provided.
142+
*
143+
* @public
144+
* @function
145+
* @static
146+
* @param {object} options
147+
* @param {object} options.config The UI5 Configuration to validate
148+
* @param {object} options.project Project information
149+
* @param {string} options.project.id ID of the project
150+
* @param {object} [options.yaml] YAML information
151+
* @param {string} options.yaml.path Path of the YAML file
152+
* @param {string} options.yaml.source Content of the YAML file
153+
* @param {number} [options.yaml.documentIndex=0] Document index in case the YAML file contains multiple documents
154+
* @throws {module:@ui5/project/validation/ValidationError}
155+
* Rejects with a {@link @ui5/project/validation/ValidationError ValidationError}
156+
* when the validation fails.
157+
* @returns {Promise<options>} Returns a Promise that resolves when the validation succeeds
158+
*/
159+
export async function getDefaults(options) {
160+
return await _validateAndSetDefaults("ui5", options);
161+
}
162+
163+
/**
164+
* Enhances bundleDefinition by adding missing properties with their respective default values.
165+
*
166+
* @param {object[]} bundles Bundles to be enhanced
167+
* @param {module:@ui5/builder/processors/bundlers/moduleBundler~ModuleBundleDefinition} bundles[].bundleDefinition
168+
* Module bundle definition
169+
* @param {module:@ui5/builder/processors/bundlers/moduleBundler~ModuleBundleOptions} [bundles[].bundleOptions]
170+
* Module bundle options
171+
* @param {module:@ui5/project/specifications/Project} project The project to get metadata from
172+
* @returns {Promise<object>} The enhanced BundleDefinition & BundleOptions
173+
*/
174+
export async function enhanceBundlesWithDefaults(bundles, project) {
175+
const config = {
176+
specVersion: `${project.getSpecVersion()}`,
177+
type: `${project.getType()}`,
178+
metadata: {name: project.getName()},
179+
builder: {bundles}
180+
};
181+
const result = await getDefaults({config, project: {id: project.getName()}});
182+
183+
return result.config.builder.bundles;
184+
}
185+
117186
/**
118187
* Validates the given ui5-workspace configuration.
119188
*

test/lib/build/definitions/application.js

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ function getMockProject() {
3131
}
3232

3333
test.beforeEach((t) => {
34+
t.context.project = getMockProject();
3435
t.context.taskUtil = {
36+
getProject: sinon.stub().returns(t.context.project),
3537
isRootProject: sinon.stub().returns(true),
3638
getBuildOption: sinon.stub(),
3739
getInterface: sinon.stub()
3840
};
3941

40-
t.context.project = getMockProject();
4142
t.context.getTask = sinon.stub();
4243
});
4344

@@ -198,12 +199,14 @@ test("Custom bundles", async (t) => {
198199
"project/b/sectionsA/",
199200
"!project/b/sectionsA/section2**",
200201
]
201-
}],
202-
sort: true
202+
}]
203203
},
204204
bundleOptions: {
205205
optimize: true,
206-
usePredefinedCalls: true
206+
usePredefineCalls: true,
207+
addTryCatchRestartWrapper: false,
208+
decorateBootstrapModule: true,
209+
numberOfParts: 1,
207210
}
208211
}, {
209212
bundleDefinition: {
@@ -215,12 +218,14 @@ test("Custom bundles", async (t) => {
215218
"project/b/sectionsB/",
216219
"!project/b/sectionsB/section2**",
217220
]
218-
}],
219-
sort: true
221+
}]
220222
},
221223
bundleOptions: {
222224
optimize: false,
223-
usePredefinedCalls: true
225+
usePredefineCalls: true,
226+
addTryCatchRestartWrapper: false,
227+
decorateBootstrapModule: true,
228+
numberOfParts: 1,
224229
}
225230
}];
226231

@@ -322,13 +327,20 @@ test("Custom bundles", async (t) => {
322327
filters: [
323328
"project/b/sectionsA/",
324329
"!project/b/sectionsA/section2**",
325-
]
326-
}],
327-
sort: true
330+
],
331+
declareRawModules: false,
332+
renderer: false,
333+
resolve: false,
334+
resolveConditional: false,
335+
sort: true,
336+
}]
328337
},
329338
bundleOptions: {
330339
optimize: true,
331-
usePredefinedCalls: true
340+
usePredefineCalls: true,
341+
addTryCatchRestartWrapper: false,
342+
decorateBootstrapModule: true,
343+
numberOfParts: 1,
332344
}
333345
}
334346
}, "generateBundle task got called with correct arguments");
@@ -346,13 +358,20 @@ test("Custom bundles", async (t) => {
346358
filters: [
347359
"project/b/sectionsB/",
348360
"!project/b/sectionsB/section2**",
349-
]
350-
}],
351-
sort: true
361+
],
362+
declareRawModules: false,
363+
renderer: false,
364+
resolve: false,
365+
resolveConditional: false,
366+
sort: true,
367+
}]
352368
},
353369
bundleOptions: {
354370
optimize: false,
355-
usePredefinedCalls: true
371+
usePredefineCalls: true,
372+
addTryCatchRestartWrapper: false,
373+
decorateBootstrapModule: true,
374+
numberOfParts: 1,
356375
}
357376
}
358377
}, "generateBundle task got called with correct arguments");
@@ -415,12 +434,14 @@ test("generateComponentPreload with custom paths, excludes and custom bundle", (
415434
"project/b/sectionsA/",
416435
"!project/b/sectionsA/section2**",
417436
]
418-
}],
419-
sort: true
437+
}]
420438
},
421439
bundleOptions: {
422440
optimize: true,
423-
usePredefinedCalls: true
441+
usePredefineCalls: true,
442+
addTryCatchRestartWrapper: false,
443+
decorateBootstrapModule: true,
444+
numberOfParts: 1,
424445
}
425446
}];
426447

test/lib/build/definitions/library.js

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@ function getMockProject() {
3434
}
3535

3636
test.beforeEach((t) => {
37+
t.context.project = getMockProject();
3738
t.context.taskUtil = {
39+
getProject: sinon.stub().returns(t.context.project),
3840
isRootProject: sinon.stub().returns(true),
3941
getBuildOption: sinon.stub(),
4042
getInterface: sinon.stub()
4143
};
4244

43-
t.context.project = getMockProject();
4445
t.context.getTask = sinon.stub();
4546
});
4647

@@ -277,12 +278,14 @@ test("Custom bundles", async (t) => {
277278
"project/b/sectionsA/",
278279
"!project/b/sectionsA/section2**",
279280
]
280-
}],
281-
sort: true
281+
}]
282282
},
283283
bundleOptions: {
284284
optimize: true,
285-
usePredefinedCalls: true
285+
usePredefineCalls: true,
286+
addTryCatchRestartWrapper: false,
287+
decorateBootstrapModule: true,
288+
numberOfParts: 1,
286289
}
287290
}, {
288291
bundleDefinition: {
@@ -294,12 +297,14 @@ test("Custom bundles", async (t) => {
294297
"project/b/sectionsB/",
295298
"!project/b/sectionsB/section2**",
296299
]
297-
}],
298-
sort: true
300+
}]
299301
},
300302
bundleOptions: {
301303
optimize: false,
302-
usePredefinedCalls: true
304+
usePredefineCalls: true,
305+
addTryCatchRestartWrapper: false,
306+
decorateBootstrapModule: true,
307+
numberOfParts: 1,
303308
}
304309
}];
305310

@@ -415,13 +420,20 @@ test("Custom bundles", async (t) => {
415420
filters: [
416421
"project/b/sectionsA/",
417422
"!project/b/sectionsA/section2**",
418-
]
419-
}],
420-
sort: true
423+
],
424+
declareRawModules: false,
425+
renderer: false,
426+
resolve: false,
427+
resolveConditional: false,
428+
sort: true,
429+
}]
421430
},
422431
bundleOptions: {
423432
optimize: true,
424-
usePredefinedCalls: true
433+
usePredefineCalls: true,
434+
addTryCatchRestartWrapper: false,
435+
decorateBootstrapModule: true,
436+
numberOfParts: 1,
425437
}
426438
}
427439
}, "generateBundle task got called with correct arguments");
@@ -439,13 +451,20 @@ test("Custom bundles", async (t) => {
439451
filters: [
440452
"project/b/sectionsB/",
441453
"!project/b/sectionsB/section2**",
442-
]
443-
}],
444-
sort: true
454+
],
455+
declareRawModules: false,
456+
renderer: false,
457+
resolve: false,
458+
resolveConditional: false,
459+
sort: true,
460+
}]
445461
},
446462
bundleOptions: {
447463
optimize: false,
448-
usePredefinedCalls: true
464+
usePredefineCalls: true,
465+
addTryCatchRestartWrapper: false,
466+
decorateBootstrapModule: true,
467+
numberOfParts: 1,
449468
}
450469
}
451470
}, "generateBundle task got called with correct arguments");
@@ -508,12 +527,14 @@ test("generateComponentPreload with custom paths, excludes and custom bundle", (
508527
"project/b/sectionsA/",
509528
"!project/b/sectionsA/section2**",
510529
]
511-
}],
512-
sort: true
530+
}]
513531
},
514532
bundleOptions: {
515533
optimize: true,
516-
usePredefinedCalls: true
534+
usePredefineCalls: true,
535+
addTryCatchRestartWrapper: false,
536+
decorateBootstrapModule: true,
537+
numberOfParts: 1,
517538
}
518539
}];
519540

0 commit comments

Comments
 (0)