Skip to content

Commit fb1db6d

Browse files
matz3RandomByte
authored andcommitted
[FIX] ui5Framework: Improve error handling for duplicate lib declaration
Fixes: SAP/ui5-tooling#780
1 parent 6a40927 commit fb1db6d

File tree

2 files changed

+157
-0
lines changed

2 files changed

+157
-0
lines changed

lib/graph/helpers/ui5Framework.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,22 @@ const utils = {
192192
});
193193
await projectGraph.resolveOptionalDependencies();
194194
},
195+
checkForDuplicateFrameworkProjects(projectGraph, frameworkGraph) {
196+
// Check for duplicate framework libraries
197+
const projectGraphProjectNames = projectGraph.getProjectNames();
198+
const duplicateFrameworkProjectNames = frameworkGraph.getProjectNames()
199+
.filter((name) => projectGraphProjectNames.includes(name));
200+
201+
if (duplicateFrameworkProjectNames.length) {
202+
throw new Error(
203+
`Duplicate framework library definition(s) found in project ${projectGraph.getRoot().getName()}: ` +
204+
`${duplicateFrameworkProjectNames.join(", ")}. ` +
205+
`Framework libraries should only be referenced via ui5.yaml configuration, ` +
206+
`not in its dependencies (e.g. package.json). ` +
207+
`Note that this error could also come from transitive dependencies.`
208+
);
209+
}
210+
},
195211
ProjectProcessor
196212
};
197213

@@ -313,6 +329,8 @@ export default {
313329
await projectProcessor.addProjectToGraph(libName);
314330
}));
315331

332+
utils.checkForDuplicateFrameworkProjects(projectGraph, frameworkGraph);
333+
316334
log.verbose("Joining framework graph into project graph...");
317335
projectGraph.join(frameworkGraph);
318336
await utils.declareFrameworkDependenciesInGraph(projectGraph);

test/lib/graph/helpers/ui5Framework.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import sinonGlobal from "sinon";
55
import esmock from "esmock";
66
import DependencyTreeProvider from "../../../../lib/graph/providers/DependencyTree.js";
77
import projectGraphBuilder from "../../../../lib/graph/projectGraphBuilder.js";
8+
import Specification from "../../../../lib/specifications/Specification.js";
89

910
const __dirname = path.dirname(fileURLToPath(import.meta.url));
1011

@@ -27,6 +28,8 @@ test.beforeEach(async (t) => {
2728
getLogger: sinon.stub().returns(t.context.log)
2829
};
2930

31+
t.context.Openui5ResolverStub = sinon.stub();
32+
3033
t.context.Sapui5ResolverStub = sinon.stub();
3134
t.context.Sapui5ResolverInstallStub = sinon.stub();
3235
t.context.Sapui5ResolverStub.callsFake(() => {
@@ -480,6 +483,84 @@ test.serial("enrichProjectGraph should ignore root project without framework con
480483
t.is(projectGraph.getSize(), 1, "Project graph should remain unchanged");
481484
});
482485

486+
test.serial("enrichProjectGraph should throw error when projectGraph contains a framework library project " +
487+
"that is also defined in framework configuration", async (t) => {
488+
const {
489+
sinon, ui5Framework, utils,
490+
Sapui5ResolverResolveVersionStub, Sapui5ResolverInstallStub
491+
} = t.context;
492+
const dependencyTree = {
493+
id: "application.a",
494+
version: "1.2.3",
495+
path: applicationAPath,
496+
configuration: {
497+
specVersion: "2.0",
498+
type: "application",
499+
metadata: {
500+
name: "application.a"
501+
},
502+
framework: {
503+
name: "SAPUI5",
504+
version: "1.100.0",
505+
libraries: [{
506+
name: "sap.ui.core"
507+
}]
508+
}
509+
},
510+
dependencies: [{
511+
id: "@openui5/sap.ui.core",
512+
version: "1.99.0",
513+
path: libraryEPath,
514+
configuration: {
515+
specVersion: "2.0",
516+
type: "library",
517+
metadata: {
518+
name: "sap.ui.core"
519+
}
520+
}
521+
}]
522+
};
523+
524+
const referencedLibraries = ["sap.ui.core"];
525+
const libraryMetadata = {fake: "metadata"};
526+
527+
sinon.stub(utils, "getFrameworkLibrariesFromGraph").resolves(referencedLibraries);
528+
529+
Sapui5ResolverInstallStub.resolves({libraryMetadata});
530+
Sapui5ResolverResolveVersionStub.resolves("1.100.0");
531+
532+
sinon.stub(utils, "ProjectProcessor")
533+
.callsFake(({graph}) => {
534+
return {
535+
async addProjectToGraph() {
536+
const fakeCoreProject = await Specification.create({
537+
id: "@openui5/sap.ui.core",
538+
version: "1.100.0",
539+
modulePath: libraryEPath,
540+
configuration: {
541+
specVersion: "3.0",
542+
kind: "project",
543+
type: "library",
544+
metadata: {
545+
name: "sap.ui.core"
546+
}
547+
}
548+
});
549+
graph.addProject(fakeCoreProject);
550+
}
551+
};
552+
});
553+
554+
const provider = new DependencyTreeProvider({dependencyTree});
555+
const projectGraph = await projectGraphBuilder(provider);
556+
557+
await t.throwsAsync(ui5Framework.enrichProjectGraph(projectGraph), {
558+
message: `Duplicate framework library definition(s) found in project application.a: sap.ui.core. ` +
559+
`Framework libraries should only be referenced via ui5.yaml configuration, not in its ` +
560+
`dependencies (e.g. package.json). Note that this error could also come from transitive dependencies.`
561+
});
562+
});
563+
483564
test.serial("utils.shouldIncludeDependency", (t) => {
484565
const {utils} = t.context;
485566
// root project dependency should always be included
@@ -963,6 +1044,64 @@ test.serial("utils.declareFrameworkDependenciesInGraph: No deprecation warnings
9631044
], `Root project has correct dependencies`);
9641045
});
9651046

1047+
test("utils.checkForDuplicateFrameworkProjects: No duplicates", (t) => {
1048+
const {utils, sinon} = t.context;
1049+
1050+
const projectGraph = {
1051+
getRoot: sinon.stub().returns({
1052+
getName: sinon.stub().returns("root-project")
1053+
}),
1054+
getProjectNames: sinon.stub().returns(["lib1", "lib2", "lib3"])
1055+
};
1056+
const frameworkGraph = {
1057+
getProjectNames: sinon.stub().returns(["sap.ui.core"])
1058+
};
1059+
1060+
t.notThrows(() => utils.checkForDuplicateFrameworkProjects(projectGraph, frameworkGraph));
1061+
});
1062+
1063+
test("utils.checkForDuplicateFrameworkProjects: One duplicate", (t) => {
1064+
const {utils, sinon} = t.context;
1065+
1066+
const projectGraph = {
1067+
getRoot: sinon.stub().returns({
1068+
getName: sinon.stub().returns("root-project")
1069+
}),
1070+
getProjectNames: sinon.stub().returns(["lib1", "sap.ui.core", "lib2", "lib3"])
1071+
};
1072+
const frameworkGraph = {
1073+
getProjectNames: sinon.stub().returns(["sap.ui.core"])
1074+
};
1075+
1076+
t.throws(() => utils.checkForDuplicateFrameworkProjects(projectGraph, frameworkGraph), {
1077+
message: "Duplicate framework library definition(s) found in project root-project: " +
1078+
"sap.ui.core. " +
1079+
"Framework libraries should only be referenced via ui5.yaml configuration, not in its dependencies " +
1080+
"(e.g. package.json). Note that this error could also come from transitive dependencies."
1081+
});
1082+
});
1083+
1084+
test("utils.checkForDuplicateFrameworkProjects: Two duplicates", (t) => {
1085+
const {utils, sinon} = t.context;
1086+
1087+
const projectGraph = {
1088+
getRoot: sinon.stub().returns({
1089+
getName: sinon.stub().returns("root-project")
1090+
}),
1091+
getProjectNames: sinon.stub().returns(["lib1", "sap.ui.core", "lib2", "sap.ui.layout", "lib3"])
1092+
};
1093+
const frameworkGraph = {
1094+
getProjectNames: sinon.stub().returns(["sap.ui.core", "sap.ui.layout", "sap.m"])
1095+
};
1096+
1097+
t.throws(() => utils.checkForDuplicateFrameworkProjects(projectGraph, frameworkGraph), {
1098+
message: "Duplicate framework library definition(s) found in project root-project: " +
1099+
"sap.ui.core, sap.ui.layout. " +
1100+
"Framework libraries should only be referenced via ui5.yaml configuration, not in its dependencies " +
1101+
"(e.g. package.json). Note that this error could also come from transitive dependencies."
1102+
});
1103+
});
1104+
9661105
test.serial("ProjectProcessor: Add project to graph", async (t) => {
9671106
const {sinon} = t.context;
9681107
const {ProjectProcessor} = t.context.utils;

0 commit comments

Comments
 (0)