Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] generateThemeDesignerResources task: Create css_variables.less #730

Merged
merged 7 commits into from
Apr 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions lib/tasks/generateThemeDesignerResources.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,37 @@ const log = require("@ui5/logger").getLogger("builder:tasks:generateThemeDesigne
const libraryLessGenerator = require("../processors/libraryLessGenerator");
const {ReaderCollectionPrioritized, Resource, fsInterface} = require("@ui5/fs");

/**
* Returns a relative path from the given themeFolder to the root namespace.
*
* When combining the given themeFolder with the returned relative path it
* resolves to "/resources/". However the "/resources/" part is not important
* here as it doesn't exist within the theming engine environment where the
* UI5 resources are part of a "UI5" folder (e.g. "UI5/sap/ui/core/") that
* is next to a "Base" folder.
*
* @example
* getPathToRoot("/resources/sap/ui/foo/themes/base")
* > "../../../../../"
*
* @param {string} themeFolder Virtual path including /resources/
* @returns {string} Relative path to root namespace
*/
function getPathToRoot(themeFolder) {
// -2 for initial "/"" and "resources/"
return "../".repeat(themeFolder.split("/").length - 2);
}

/**
* Generates an less import statement for the given <code>filePath</code>
*
* @param {string} filePath The path to the desired file
* @returns {string} The less import statement
*/
function lessImport(filePath) {
return `@import "${filePath}";\n`;
}

function generateLibraryDotTheming({namespace, version, hasThemes}) {
const dotTheming = {
sEntity: "Library",
Expand Down Expand Up @@ -80,6 +111,82 @@ async function generateThemeDotTheming({workspace, combo, themeFolder}) {
return newDotThemingResource;
}

async function createCssVariablesLessResource({workspace, combo, themeFolder}) {
const pathToRoot = getPathToRoot(themeFolder);
const cssVariablesSourceLessFile = "css_variables.source.less";
const cssVariablesLessFile = "css_variables.less";

// posix as it is a virtual path (separated with /)
const themeName = posixPath.basename(themeFolder);
// The "base" theme of the baseLib is called "baseTheme"
const baseLibThemeName = themeName === "base" ? "baseTheme" : themeName;

// Some themes do not have a base.less file (e.g. sap_hcb)
const hasBaseLess = !!(await combo.byPath(`/resources/sap/ui/core/themes/${themeName}/base.less`));

let cssVariablesLess =
`/* NOTE: This file was generated as an optimized version of "${cssVariablesSourceLessFile}" \
for the Theme Designer. */\n\n`;

if (themeName !== "base") {
const cssVariablesSourceLessResource = await workspace.byPath(
posixPath.join(themeFolder, cssVariablesSourceLessFile)
);

if (!cssVariablesSourceLessResource) {
throw new Error(`Could not find file "${cssVariablesSourceLessFile}" in theme "${themeFolder}"`);
}

const cssVariablesSourceLess = await cssVariablesSourceLessResource.getString();

cssVariablesLess += lessImport(`../base/${cssVariablesLessFile}`);
cssVariablesLess += `
/* START "${cssVariablesSourceLessFile}" */
${cssVariablesSourceLess}
/* END "${cssVariablesSourceLessFile}" */

`;
}

if (hasBaseLess) {
cssVariablesLess += lessImport(`${pathToRoot}../Base/baseLib/${baseLibThemeName}/base.less`);
}
cssVariablesLess += lessImport(`${pathToRoot}sap/ui/core/themes/${themeName}/global.less`);

return new Resource({
path: posixPath.join(themeFolder, cssVariablesLessFile),
string: cssVariablesLess
});
}

async function generateCssVariablesLess({workspace, combo, namespace}) {
let cssVariablesSourceLessResourcePattern;
if (namespace) {
// In case of a library only check for themes directly below the namespace
cssVariablesSourceLessResourcePattern = `/resources/${namespace}/themes/*/css_variables.source.less`;
} else {
// In case of a theme-library check for all "themes"
cssVariablesSourceLessResourcePattern = `/resources/**/themes/*/css_variables.source.less`;
}

const cssVariablesSourceLessResource = await workspace.byGlob(cssVariablesSourceLessResourcePattern);

const hasCssVariables = cssVariablesSourceLessResource.length > 0;

if (hasCssVariables) {
await Promise.all(
cssVariablesSourceLessResource.map(async (cssVariableSourceLess) => {
const themeFolder = posixPath.dirname(cssVariableSourceLess.getPath());
log.verbose(`Generating css_variables.less for theme ${themeFolder}`);
const r = await createCssVariablesLessResource({
workspace, combo, themeFolder
});
return await workspace.write(r);
})
);
}
}

/**
* Generates resources required for integration with the SAP Theme Designer.
*
Expand Down Expand Up @@ -163,4 +270,7 @@ module.exports = async function({workspace, dependencies, options: {projectName,
await Promise.all(
libraryLessResources.map((resource) => workspace.write(resource))
);

// css_variables.less
await generateCssVariablesLess({workspace, combo, namespace});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sEntity": "Library",
"sId": "theme/j",
"sVersion": "1.0.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"sEntity": "Theme",
"sId": "somefancytheme",
"sVendor": "SAP",
"oExtends": "base"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.someClass {
color: @someColor
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:root{--someColor:#000}
/* Inline theming parameters */
#sap-ui-theme-theme\.j{background-image:url('data:text/plain;utf-8,%7B%22someColor%22%3A%22%23000%22%7D')}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* NOTE: This file was generated as an optimized version of "css_variables.source.less" for the Theme Designer. */

@import "../base/css_variables.less";

/* START "css_variables.source.less" */
@someColor: #000000;

:root {
--someColor: @someColor;
}

/* END "css_variables.source.less" */

@import "../../../../sap/ui/core/themes/somefancytheme/global.less";
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@someColor: #000000;

:root {
--someColor: @someColor;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.someClass{color:#000}
/* Inline theming parameters */
#sap-ui-theme-theme\.j{background-image:url('data:text/plain;utf-8,%7B%22someColor%22%3A%22%23000%22%7D')}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"someColor":"#000"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.someClass{color:#000}
/* Inline theming parameters */
#sap-ui-theme-theme\.j{background-image:url('data:text/plain;utf-8,%7B%22someColor%22%3A%22%23000%22%7D')}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* NOTE: This file was generated as an optimized version of "library.source.less" for the Theme Designer. */

@someColor: black;
/* START "Button.less" */
.someClass {
color: @someColor
}

/* END "Button.less" */

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@someColor: black;
@import "Button.less";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.someClass{color:var(--someColor)}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.someClass{color:var(--someColor)}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<theme xmlns="http://www.sap.com/sap.ui.library.xsd" >

<name>my_theme</name>
<vendor>me</vendor>
<copyright>Some fancy copyright</copyright>
<version>1.0.0</version>

</theme>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"sEntity": "Theme",
"sId": "my_theme",
"sVendor": "SAP",
"oExtends": "base"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:root{--mycolor:#00f}
/* Inline theming parameters */
#sap-ui-theme-theme\.library\.e{background-image:url('data:text/plain;utf-8,%7B%22mycolor%22%3A%22%2300f%22%7D')}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* NOTE: This file was generated as an optimized version of "css_variables.source.less" for the Theme Designer. */

@import "../base/css_variables.less";

/* START "css_variables.source.less" */
@mycolor: #0000ff;

:root {
--mycolor: @mycolor;
}

/* END "css_variables.source.less" */

@import "../../../../../sap/ui/core/themes/my_theme/global.less";
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@mycolor: #0000ff;

:root {
--mycolor: @mycolor;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/*!
* Some fancy copyright
*/.sapUiBody{background-color:#00f}
/* Inline theming parameters */
#sap-ui-theme-theme\.library\.e{background-image:url('data:text/plain;utf-8,%7B%22mycolor%22%3A%22%2300f%22%7D')}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"mycolor":"#00f"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/*!
* Some fancy copyright
*/.sapUiBody{background-color:#00f}
/* Inline theming parameters */
#sap-ui-theme-theme\.library\.e{background-image:url('data:text/plain;utf-8,%7B%22mycolor%22%3A%22%2300f%22%7D')}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* NOTE: This file was generated as an optimized version of "library.source.less" for the Theme Designer. */

/*!
* Some fancy copyright
*/

@mycolor: blue;

.sapUiBody {
background-color: @mycolor;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*!
* Some fancy copyright
*/

@mycolor: blue;

.sapUiBody {
background-color: @mycolor;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/*!
* Some fancy copyright
*/.sapUiBody{background-color:var(--mycolor)}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/*!
* Some fancy copyright
*/.sapUiBody{background-color:var(--mycolor)}
40 changes: 40 additions & 0 deletions test/lib/builder/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,26 @@ test.serial("Build library with theme configured for CSS variables", (t) => {
});
});

test.serial("Build library with theme configured for CSS variables and theme designer resources", (t) => {
const destPath = "./test/tmp/build/theme.j/dest-css-variables-theme-designer-resources";
const expectedPath = "./test/expected/build/theme.j/dest-css-variables-theme-designer-resources";
return builder.build({
tree: themeJTree,
cssVariables: true,
destPath,
includedTasks: ["generateThemeDesignerResources"]
}).then(() => {
return findFiles(expectedPath);
}).then((expectedFiles) => {
// Check for all directories and files
assert.directoryDeepEqual(destPath, expectedPath);

return checkFileContentsIgnoreLineFeeds(t, expectedFiles, expectedPath, destPath);
}).then(() => {
t.pass();
});
});

test.serial("Build theme-library with CSS variables", (t) => {
const destPath = "./test/tmp/build/theme.library.e/dest-css-variables";
const expectedPath = "./test/expected/build/theme.library.e/dest-css-variables";
Expand All @@ -971,6 +991,26 @@ test.serial("Build theme-library with CSS variables", (t) => {
});
});

test.serial("Build theme-library with CSS variables and theme designer resources", (t) => {
const destPath = "./test/tmp/build/theme.library.e/dest-css-variables-theme-designer-resources";
const expectedPath = "./test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources";
return builder.build({
tree: themeLibraryETree,
cssVariables: true,
destPath,
includedTasks: ["generateThemeDesignerResources"]
}).then(() => {
return findFiles(expectedPath);
}).then((expectedFiles) => {
// Check for all directories and files
assert.directoryDeepEqual(destPath, expectedPath);

return checkFileContentsIgnoreLineFeeds(t, expectedFiles, expectedPath, destPath);
}).then(() => {
t.pass();
});
});

test.serial("Cleanup", async (t) => {
const BuildContext = require("../../../lib/builder/BuildContext");
const createProjectContextStub = sinon.spy(BuildContext.prototype, "createProjectContext");
Expand Down
Loading