Skip to content

Commit

Permalink
[INTERNAL] ComponentProject: Allow writing resources outside of names…
Browse files Browse the repository at this point in the history
…pace
  • Loading branch information
RandomByte committed Jun 1, 2022
1 parent 46968e4 commit 6bae282
Show file tree
Hide file tree
Showing 16 changed files with 465 additions and 111 deletions.
129 changes: 71 additions & 58 deletions lib/specifications/ComponentProject.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,32 +101,27 @@ class ComponentProject extends Project {
* @returns {module:@ui5/fs.ReaderCollection} A reader collection instance
*/
getReader({style = "buildtime"} = {}) {
// TODO: Additional parameter 'includeWorkspace' to include reader to relevant Memory Adapter?
// TODO: Additional style 'ABAP' using "sap.platform.abap".uri from manifest.json?

if (style === "runtime" && this._isRuntimeNamespaced) {
// If the project's runtime requires namespaces, paths are identical to "buildtime" style
style = "buildtime";
}
let reader;
let testReader;
switch (style) {
case "buildtime":
reader = this._getFlatSourceReader(`/resources/${this._namespace}/`);
testReader = this._getFlatTestReader(`/test-resources/${this._namespace}/`);
if (testReader) {
reader = resourceFactory.createReaderCollection({
name: `Reader collection for project ${this.getName()}`,
readers: [reader, testReader]
});
}
reader = this._getReader();
break;
case "runtime":
if (this._isRuntimeNamespaced) {
// Same as buildtime
return this.getReader();
}
// TODO 3.0: Refactor this
return this.getReader().link({
// No test-resources for runtime resource access,
// unless runtime is namespaced
reader = this.getReader().link({
linkPath: `/`,
targetPath: `/resources/${this._namespace}/`
});
break;
case "flat":
// No test-resources for flat resource access
reader = this._getFlatSourceReader("/");
break;
default:
Expand All @@ -138,16 +133,7 @@ class ComponentProject extends Project {
}

/**
* Get a resource reader for the sources of the project (not including any test resources)
*
* @returns {module:@ui5/fs.ReaderCollection} Reader collection
*/
_getFullSourceReader() {
throw new Error(`_getFullSourceReader must be implemented by subclass ${this.constructor.name}`);
}

/**
* TODO
* Get a resource reader for the resources of the project
*
* @returns {module:@ui5/fs.ReaderCollection} Reader collection
*/
Expand All @@ -156,15 +142,7 @@ class ComponentProject extends Project {
}

/**
* Get a resource reader for the test-sources of the project
*
* @returns {module:@ui5/fs.ReaderCollection} Reader collection
*/
_getFullTestReader() {
throw new Error(`_getFullTestReader must be implemented by subclass ${this.constructor.name}`);
}
/**
* TODO
* Get a resource reader for the test resources of the project
*
* @returns {module:@ui5/fs.ReaderCollection} Reader collection
*/
Expand All @@ -180,63 +158,98 @@ class ComponentProject extends Project {
*/
getWorkspace() {
// Workspace is always of style "buildtime"
const reader = this.getReader({
style: "buildtime"
});

const writer = this._getWriter();
return resourceFactory.createWorkspace({
reader,
writer
name: `Workspace for project ${this.getName()}`,
reader: this._getReader(),
writer: this._getWriter().collection
});
}

_getWriter() {
if (!this._writer) {
if (!this._writers) {
// writer is always of style "buildtime"
this._writer = resourceFactory.createAdapter({
const namespaceWriter = resourceFactory.createAdapter({
virBasePath: "/",
project: this
});

const generalWriter = resourceFactory.createAdapter({
virBasePath: "/",
project: this
});

const collection = resourceFactory.createWriterCollection({
name: `Writers for project ${this.getName()}`,
writerMapping: {
[`/resources/${this._namespace}/`]: namespaceWriter,
[`/test-resources/${this._namespace}/`]: namespaceWriter,
[`/`]: generalWriter
}
});

this._writers = {
namespaceWriter,
generalWriter,
collection
};
}
return this._writers;
}

_getReader() {
let reader = this._getFlatSourceReader(`/resources/${this._namespace}/`);
const testReader = this._getFlatTestReader(`/test-resources/${this._namespace}/`);
if (testReader) {
reader = resourceFactory.createReaderCollection({
name: `Reader collection for project ${this.getName()}`,
readers: [reader, testReader]
});
}
return this._writer;
return reader;
}

_addWriter(reader, style = "buildtime") {
let writer = this._getWriter();
_addWriter(reader, style) {
const {namespaceWriter, generalWriter} = this._getWriter();

if (style === "runtime" && this._isRuntimeNamespaced) {
// If the project's runtime requires namespaces, "runtime" paths are identical to "buildtime" paths
style = "buildtime";
}
const readers = [];
switch (style) {
case "buildtime": {
// Writer already uses buildtime style
readers.push(namespaceWriter);
readers.push(generalWriter);
break;
}
case "runtime": {
if (this._isRuntimeNamespaced) {
// Same as buildtime
return this._addWriter(reader);
}

// Rewrite paths from "runtime" to "buildtime"
writer = writer.link({
// Runtime is not namespaced: link namespace to /
readers.push(namespaceWriter.link({
linkPath: `/`,
targetPath: `/resources/${this._namespace}/`
});
}));
// Add general writer as is
readers.push(generalWriter);
break;
}
case "flat": {
// Rewrite paths from "flat" to "buildtime"
writer = writer.link({
readers.push(namespaceWriter.link({
linkPath: `/`,
targetPath: `/resources/${this._namespace}/`
});
}));
// General writer resources can't be flattened, so they are not available
break;
}
default:
throw new Error(`Unknown path mapping style ${style}`);
}
readers.push(reader);

return resourceFactory.createReaderCollectionPrioritized({
name: `Reader/Writer collection for project ${this.getName()}`,
readers: [writer, reader]
readers
});
}

Expand Down
4 changes: 2 additions & 2 deletions lib/specifications/types/Application.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Application extends ComponentProject {
return null; // Applications do not have a dedicated test directory
}

_getSourceReader() {
_getRawSourceReader() {
return resourceFactory.createReader({
fsBasePath: fsPath.join(this.getPath(), this._webappPath),
virBasePath: "/",
Expand Down Expand Up @@ -190,7 +190,7 @@ class Application extends ComponentProject {
if (this._pManifests[filePath]) {
return this._pManifests[filePath];
}
return this._pManifests[filePath] = this._getSourceReader().byPath(filePath)
return this._pManifests[filePath] = this._getRawSourceReader().byPath(filePath)
.then(async (resource) => {
if (!resource) {
throw new Error(
Expand Down
68 changes: 41 additions & 27 deletions lib/specifications/types/Library.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class Library extends ComponentProject {
this._srcPath = "src";
this._testPath = "test";
this._testPathExists = false;
this._isSourceNamespaced = true;

this._propertiesFilesSourceEncoding = "UTF-8";
}

Expand All @@ -29,27 +31,14 @@ class Library extends ComponentProject {
}

/* === Resource Access === */

/**
*
* Get a resource reader for the sources of the project (excluding any test resources)
* In the future the path structure can be flat or namespaced depending on the project
*
* @returns {module:@ui5/fs.ReaderCollection} Reader collection
*/
_getSourceReader() {
return resourceFactory.createReader({
fsBasePath: fsPath.join(this.getPath(), this._srcPath),
virBasePath: "/",
name: `Source reader for library project ${this.getName()}`,
project: this
});
}

_getFlatSourceReader(virBasePath = "/") {
// TODO: Throw for libraries with additional namespaces like sap.ui.core?
let fsBasePath = fsPath.join(this.getPath(), this._srcPath);
if (this._isSourceNamespaced) {
fsBasePath = fsPath.join(fsBasePath, ...this._namespace.split("/"));
}
return resourceFactory.createReader({
fsBasePath: fsPath.join(this.getPath(), this._srcPath, ...this._namespace.split("/")),
fsBasePath,
virBasePath,
name: `Source reader for library project ${this.getName()}`,
project: this
Expand All @@ -60,15 +49,35 @@ class Library extends ComponentProject {
if (!this._testPathExists) {
return null;
}
let fsBasePath = fsPath.join(this.getPath(), this._testPath);
if (this._isSourceNamespaced) {
fsBasePath = fsPath.join(fsBasePath, ...this._namespace.split("/"));
}
const testReader = resourceFactory.createReader({
fsBasePath: fsPath.join(this.getPath(), this._testPath, ...this._namespace.split("/")),
fsBasePath,
virBasePath,
name: `Runtime test-resources reader for library project ${this.getName()}`,
project: this
});
return testReader;
}

/**
*
* Get a resource reader for the sources of the project (excluding any test resources)
* In the future the path structure can be flat or namespaced depending on the project
*
* @returns {module:@ui5/fs.ReaderCollection} Reader collection
*/
_getRawSourceReader() {
return resourceFactory.createReader({
fsBasePath: fsPath.join(this.getPath(), this._srcPath),
virBasePath: "/",
name: `Source reader for library project ${this.getName()}`,
project: this
});
}

/* === Internals === */
/**
* @private
Expand Down Expand Up @@ -246,11 +255,16 @@ class Library extends ComponentProject {
}

namespace = libraryNs.replace(/\./g, "/");
namespacePath = namespacePath.replace("/", ""); // remove leading slash
if (namespacePath !== namespace) {
throw new Error(
`Detected namespace "${namespace}" does not match detected directory ` +
`structure "${namespacePath}" for project ${this.getName()}`);
if (namespacePath === "/") {
this._log.verbose(`Detected flat library source structure for project ${this.getName()}`);
this._isSourceNamespaced = false;
} else {
namespacePath = namespacePath.replace("/", ""); // remove leading slash
if (namespacePath !== namespace) {
throw new Error(
`Detected namespace "${namespace}" does not match detected directory ` +
`structure "${namespacePath}" for project ${this.getName()}`);
}
}
} else {
try {
Expand Down Expand Up @@ -384,7 +398,7 @@ class Library extends ComponentProject {
if (this._pManifest) {
return this._pManifest;
}
return this._pManifest = this._getSourceReader().byGlob("**/manifest.json")
return this._pManifest = this._getRawSourceReader().byGlob("**/manifest.json")
.then(async (manifestResources) => {
if (!manifestResources.length) {
throw new Error(`Could not find manifest.json file for project ${this.getName()}`);
Expand Down Expand Up @@ -416,7 +430,7 @@ class Library extends ComponentProject {
if (this._pDotLibrary) {
return this._pDotLibrary;
}
return this._pDotLibrary = this._getSourceReader().byGlob("**/.library")
return this._pDotLibrary = this._getRawSourceReader().byGlob("**/.library")
.then(async (dotLibraryResources) => {
if (!dotLibraryResources.length) {
throw new Error(`Could not find .library file for project ${this.getName()}`);
Expand Down Expand Up @@ -456,7 +470,7 @@ class Library extends ComponentProject {
if (this._pLibraryJs) {
return this._pLibraryJs;
}
return this._pLibraryJs = this._getSourceReader().byGlob("**/library.js")
return this._pLibraryJs = this._getRawSourceReader().byGlob("**/library.js")
.then(async (libraryJsResources) => {
if (!libraryJsResources.length) {
throw new Error(`Could not find library.js file for project ${this.getName()}`);
Expand Down
1 change: 1 addition & 0 deletions lib/specifications/types/ThemeLibrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class ThemeLibrary extends Project {

this._srcPath = "src";
this._testPath = "test";
this._testPathExists = false;
this._writer = null;
}

Expand Down
11 changes: 11 additions & 0 deletions test/fixtures/library.h/src/.library
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<library xmlns="http://www.sap.com/sap.ui.library.xsd" >

<name>library.h</name>
<vendor>SAP SE</vendor>
<copyright>${copyright}</copyright>
<version>${version}</version>

<documentation>Library G</documentation>

</library>
26 changes: 26 additions & 0 deletions test/fixtures/library.h/src/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"_version": "1.21.0",
"sap.app": {
"id": "library.h",
"type": "library",
"embeds": [],
"applicationVersion": {
"version": "1.0.0"
},
"title": "Library H",
"description": "Library H"
},
"sap.ui": {
"technology": "UI5",
"supportedThemes": []
},
"sap.ui5": {
"dependencies": {
"minUI5Version": "1.0",
"libs": {}
},
"library": {
"i18n": false
}
}
}
4 changes: 4 additions & 0 deletions test/fixtures/library.h/src/some.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/*!
* ${copyright}
*/
console.log('HelloWorld');

0 comments on commit 6bae282

Please sign in to comment.