Skip to content

Commit c2784f7

Browse files
committed
Add shim extension
1 parent 5c6f688 commit c2784f7

File tree

9 files changed

+297
-54
lines changed

9 files changed

+297
-54
lines changed

lib/projectPreprocessor.js

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const typeRepository = require("@ui5/builder").types.typeRepository;
99
class ProjectPreprocessor {
1010
constructor() {
1111
this.processedProjects = {};
12+
this.configShims = {};
13+
this.collections = {};
1214
}
1315

1416
/*
@@ -64,7 +66,7 @@ class ProjectPreprocessor {
6466
}
6567

6668
await this.loadProjectConfiguration(project);
67-
// this.applyShims(project); // shims not yet implemented
69+
this.applyShims(project);
6870
if (this.isConfigValid(project)) {
6971
await this.applyType(project);
7072
queue.push({
@@ -292,8 +294,20 @@ class ProjectPreprocessor {
292294
await type.format(project);
293295
}
294296

295-
async applyExtension(project) {
296-
// TOOD
297+
async applyExtension(extension) {
298+
log.verbose(`Applying extension ${extension.id}...`);
299+
300+
if (!extension.metadata || !extension.metadata.name) {
301+
throw new Error(`metadata.name configuration is missing for extension ${extension.id}`);
302+
} // TODO: more checks? Version?
303+
304+
switch (extension.type) {
305+
case "project-shim":
306+
this.handleShim(extension);
307+
break;
308+
default:
309+
throw new Error(`Unknown extension type '${extension.type}' for ${extension.id}`);
310+
}
297311
}
298312

299313
async readConfigFile(configPath) {
@@ -302,6 +316,100 @@ class ProjectPreprocessor {
302316
filename: path
303317
});
304318
}
319+
320+
handleShim(extension) {
321+
if (extension.configurations) {
322+
const configurations = extension.configurations;
323+
log.verbose(`Project shim ${extension.id} contains ` +
324+
`${Object.keys(configurations)} configuration(s)`);
325+
for (let projectId in configurations) {
326+
if (configurations.hasOwnProperty(projectId)) {
327+
this.normalizeConfig(configurations[projectId]); // TODO: Clone object beforehand?
328+
if (this.configShims[projectId]) {
329+
log.verbose(`Project shim ${extension.id}: A configuration shim for project ${projectId} `+
330+
"has already been applied. Skipping.");
331+
} else if (this.isConfigValid(configurations[projectId])) {
332+
log.verbose(`Project shim ${extension.id}: Adding project configuration for ${projectId}...`);
333+
this.configShims[projectId] = configurations[projectId];
334+
} else {
335+
log.verbose(`Project shim ${extension.id}: Ignoring invalid ` +
336+
`configuration shim for project ${projectId}`);
337+
}
338+
}
339+
}
340+
}
341+
342+
if (extension.dependencies) {
343+
// For the time being, shimmed dependencies only apply to shimmed project configurations
344+
const dependencies = extension.dependencies;
345+
for (let projectId in dependencies) {
346+
if (dependencies.hasOwnProperty(projectId)) {
347+
if (this.configShims[projectId]) {
348+
log.verbose(`Project shim ${extension.id}: Adding dependencies ` +
349+
`to project shim '${projectId}'...`);
350+
this.configShims[projectId].dependencies = dependencies[projectId];
351+
} else {
352+
log.verbose(`Project shim ${extension.id}: No configuration shim found for ` +
353+
`project ID '${projectId}'. Dependency shims currently only apply ` +
354+
"to projects with configuration shims.");
355+
}
356+
}
357+
}
358+
}
359+
360+
if (extension.collections) {
361+
const collections = extension.collections;
362+
log.verbose(`Project shim ${extension.id} contains ` +
363+
`${Object.keys(collections)} collection(s)`);
364+
for (let projectId in collections) {
365+
if (collections.hasOwnProperty(projectId)) {
366+
if (this.collections[projectId]) {
367+
log.verbose(`Project shim ${extension.id}: A collection with id '${projectId}' `+
368+
"is already known. Skipping.");
369+
} else {
370+
log.verbose(`Project shim ${extension.id}: Adding collection with id '${projectId}'...`);
371+
this.collections[projectId] = collections[projectId];
372+
}
373+
}
374+
}
375+
}
376+
}
377+
378+
applyShims(project) {
379+
// Apply configuration shims
380+
if (this.configShims[project.id]) {
381+
log.verbose(`Applying configuration shim for project ${project.id}...`);
382+
const configShim = JSON.parse(JSON.stringify(this.configShims[project.id]));
383+
Object.assign(project, configShim);
384+
}
385+
386+
// Apply collections
387+
for (let i = project.dependencies.length - 1; i >= 0; i--) {
388+
const depId = project.dependencies[i];
389+
if (this.collections[depId]) {
390+
log.verbose(`Project ${project.id} depends on collection ${depId}. Resolving...`);
391+
// This project contains on a collection
392+
// => replace collection dependency with first collection project.
393+
const collectionDep = project.dependencies[i];
394+
const collection = this.collections[depId];
395+
const projects = [];
396+
for (let projectId in collection) {
397+
if (collection.hasOwnProperty(projectId)) {
398+
// Clone and modify collection "project"
399+
const project = JSON.parse(JSON.stringify(collectionDep));
400+
project.id = projectId;
401+
project.path = path.join(project.path, collection[projectId]);
402+
projects.push(project);
403+
}
404+
}
405+
406+
// Use first collection project to replace the collection dependency
407+
project.dependencies[i] = projects.shift();
408+
// Add any additional collection projects to end of dependency array (already processed)
409+
project.dependencies.push(...projects);
410+
}
411+
}
412+
}
305413
}
306414

307415
/**

test/fixtures/legacy.library.a/node_modules/library.f/package.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/legacy.library.a/node_modules/library.f/ui5.yaml

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "legacy.library.a",
3+
"version": "1.0.0",
4+
"description": "Simple SAPUI5 based library - legacy library missing ui5.yaml",
5+
"devDependencies": {
6+
},
7+
"scripts": {
8+
"test": "echo \"Error: no test specified\" && exit 1"
9+
}
10+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<library xmlns="http://www.sap.com/sap.ui.library.xsd" >
3+
4+
<name>legacy.library.a</name>
5+
<vendor>SAP SE</vendor>
6+
<copyright>${copyright}</copyright>
7+
<version>${version}</version>
8+
9+
<documentation>Legacy Library A</documentation>
10+
11+
</library>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/*!
2+
* ${copyright}
3+
*/
4+
console.log('HelloWorld');

test/fixtures/legacy.library.a/test/legacy/library/a/Test.html

Whitespace-only changes.

test/lib/extensions.js

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
const {test} = require("ava");
2+
const path = require("path");
3+
const projectPreprocessor = require("../..").projectPreprocessor;
4+
const applicationAPath = path.join(__dirname, "..", "fixtures", "application.a");
5+
const legacyLibraryAPath = path.join(__dirname, "..", "fixtures", "legacy.library.a");
6+
7+
test("Projects with extension dependency inline configuration", (t) => {
8+
const tree = {
9+
id: "application.a",
10+
path: applicationAPath,
11+
dependencies: [{
12+
id: "extension.a",
13+
path: applicationAPath,
14+
dependencies: [],
15+
version: "1.0.0",
16+
specVersion: "0.1",
17+
kind: "extension",
18+
type: "project-shim",
19+
metadata: {
20+
name: "shims.a"
21+
},
22+
configurations: {
23+
"legacy.library.a": {
24+
specVersion: "0.1",
25+
type: "library",
26+
metadata: {
27+
name: "legacy.library.a",
28+
}
29+
}
30+
}
31+
}, {
32+
id: "legacy.library.a",
33+
version: "1.0.0",
34+
path: legacyLibraryAPath,
35+
dependencies: []
36+
}],
37+
version: "1.0.0",
38+
specVersion: "0.1",
39+
type: "application",
40+
metadata: {
41+
name: "xy"
42+
}
43+
};
44+
return projectPreprocessor.processTree(tree).then((parsedTree) => {
45+
t.deepEqual(parsedTree, {
46+
_level: 0,
47+
type: "application",
48+
metadata: {
49+
name: "xy",
50+
},
51+
resources: {
52+
configuration: {
53+
paths: {
54+
webapp: "webapp"
55+
}
56+
},
57+
pathMappings: {
58+
"/": "webapp",
59+
}
60+
},
61+
dependencies: [{
62+
id: "legacy.library.a",
63+
kind: "project",
64+
version: "1.0.0",
65+
specVersion: "0.1",
66+
path: legacyLibraryAPath,
67+
_level: 1,
68+
type: "library",
69+
metadata: {
70+
name: "legacy.library.a",
71+
copyright: "${copyright}",
72+
},
73+
resources: {
74+
configuration: {
75+
paths: {
76+
src: "src",
77+
test: "test"
78+
}
79+
},
80+
pathMappings: {
81+
"/resources/": "src",
82+
"/test-resources/": "test"
83+
}
84+
},
85+
dependencies: []
86+
}],
87+
id: "application.a",
88+
kind: "project",
89+
version: "1.0.0",
90+
specVersion: "0.1",
91+
path: applicationAPath
92+
}, "Parsed correctly");
93+
});
94+
});
95+
96+
test("Projects with extension dependency inline configuration", (t) => {
97+
const tree = {
98+
id: "application.a",
99+
path: applicationAPath,
100+
dependencies: [{
101+
id: "extension.a",
102+
path: applicationAPath,
103+
dependencies: [],
104+
version: "1.0.0",
105+
specVersion: "0.1",
106+
kind: "extension",
107+
type: "project-type",
108+
metadata: {
109+
name: "z"
110+
}
111+
}],
112+
version: "1.0.0",
113+
specVersion: "0.1",
114+
type: "z",
115+
metadata: {
116+
name: "xy"
117+
}
118+
};
119+
return t.throws(projectPreprocessor.processTree(tree).then((parsedTree) => {
120+
t.deepEqual(parsedTree, {
121+
_level: 0,
122+
type: "z",
123+
metadata: {
124+
name: "xy",
125+
},
126+
resources: {
127+
configuration: {
128+
paths: {
129+
root: ""
130+
}
131+
},
132+
pathMappings: {
133+
"/": "",
134+
}
135+
},
136+
dependencies: [],
137+
id: "application.a",
138+
kind: "project",
139+
version: "1.0.0",
140+
specVersion: "0.1",
141+
path: applicationAPath
142+
}, "Parsed correctly");
143+
}), "Unknown extension type 'project-type' for extension.a", "Rejected with error");
144+
});

test/lib/projectPreprocessor.js

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -48,57 +48,6 @@ test("Project with inline configuration", (t) => {
4848
});
4949
});
5050

51-
test("Projects with extension dependency inline configuration", (t) => {
52-
const tree = {
53-
id: "application.a",
54-
path: applicationAPath,
55-
dependencies: [{
56-
id: "extension.a",
57-
path: applicationAPath,
58-
dependencies: [],
59-
version: "1.0.0",
60-
specVersion: "0.1",
61-
kind: "extension",
62-
type: "project-type",
63-
metadata: {
64-
name: "z"
65-
}
66-
}],
67-
version: "1.0.0",
68-
specVersion: "0.1",
69-
type: "application",
70-
metadata: {
71-
name: "xy"
72-
}
73-
};
74-
return projectPreprocessor.processTree(tree).then((parsedTree) => {
75-
t.deepEqual(parsedTree, {
76-
_level: 0,
77-
type: "application",
78-
metadata: {
79-
name: "xy",
80-
},
81-
resources: {
82-
configuration: {
83-
paths: {
84-
webapp: "webapp"
85-
}
86-
},
87-
pathMappings: {
88-
"/": "webapp",
89-
}
90-
},
91-
dependencies: [],
92-
id: "application.a",
93-
kind: "project",
94-
version: "1.0.0",
95-
specVersion: "0.1",
96-
path: applicationAPath
97-
}, "Parsed correctly");
98-
});
99-
});
100-
101-
10251
test("Project with configPath", (t) => {
10352
const tree = {
10453
id: "application.a",
@@ -1036,6 +985,7 @@ test("Library version in package.json data is missing", (t) => {
1036985
const tree = {
1037986
id: "library.d",
1038987
path: libraryDPath,
988+
dependencies: [],
1039989
type: "library",
1040990
metadata: {
1041991
name: "library.d"

0 commit comments

Comments
 (0)