diff --git a/lib/tasks/generateThemeDesignerResources.js b/lib/tasks/generateThemeDesignerResources.js index 94cf720b6..f8f75c4fa 100644 --- a/lib/tasks/generateThemeDesignerResources.js +++ b/lib/tasks/generateThemeDesignerResources.js @@ -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 filePath + * + * @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", @@ -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. * @@ -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}); }; diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/.theming b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/.theming new file mode 100644 index 000000000..d4edad9a4 --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/.theming @@ -0,0 +1,5 @@ +{ + "sEntity": "Library", + "sId": "theme/j", + "sVersion": "1.0.0" +} \ No newline at end of file diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/.theming b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/.theming new file mode 100644 index 000000000..4878adda8 --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/.theming @@ -0,0 +1,6 @@ +{ + "sEntity": "Theme", + "sId": "somefancytheme", + "sVendor": "SAP", + "oExtends": "base" +} \ No newline at end of file diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/Button.less b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/Button.less new file mode 100644 index 000000000..ca968183f --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/Button.less @@ -0,0 +1,3 @@ +.someClass { + color: @someColor +} diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/css_variables.css b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/css_variables.css new file mode 100644 index 000000000..6232d9e35 --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/css_variables.css @@ -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')} diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/css_variables.less b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/css_variables.less new file mode 100644 index 000000000..91ce28694 --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/css_variables.less @@ -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"; diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/css_variables.source.less b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/css_variables.source.less new file mode 100644 index 000000000..5a6ce08e9 --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/css_variables.source.less @@ -0,0 +1,5 @@ +@someColor: #000000; + +:root { +--someColor: @someColor; +} diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library-RTL.css b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library-RTL.css new file mode 100644 index 000000000..5009ca50e --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library-RTL.css @@ -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')} diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library-parameters.json b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library-parameters.json new file mode 100644 index 000000000..a190cda03 --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library-parameters.json @@ -0,0 +1 @@ +{"someColor":"#000"} \ No newline at end of file diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library.css b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library.css new file mode 100644 index 000000000..5009ca50e --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library.css @@ -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')} diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library.less b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library.less new file mode 100644 index 000000000..24c4ca167 --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library.less @@ -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" */ + diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library.source.less b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library.source.less new file mode 100644 index 000000000..834de919e --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library.source.less @@ -0,0 +1,2 @@ +@someColor: black; +@import "Button.less"; diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library_skeleton-RTL.css b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library_skeleton-RTL.css new file mode 100644 index 000000000..7db086289 --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library_skeleton-RTL.css @@ -0,0 +1 @@ +.someClass{color:var(--someColor)} \ No newline at end of file diff --git a/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library_skeleton.css b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library_skeleton.css new file mode 100644 index 000000000..7db086289 --- /dev/null +++ b/test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library_skeleton.css @@ -0,0 +1 @@ +.someClass{color:var(--someColor)} \ No newline at end of file diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/.theme b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/.theme new file mode 100644 index 000000000..4b4b1cf98 --- /dev/null +++ b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/.theme @@ -0,0 +1,9 @@ + + + + my_theme + me + Some fancy copyright + 1.0.0 + + \ No newline at end of file diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/.theming b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/.theming new file mode 100644 index 000000000..184073476 --- /dev/null +++ b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/.theming @@ -0,0 +1,6 @@ +{ + "sEntity": "Theme", + "sId": "my_theme", + "sVendor": "SAP", + "oExtends": "base" +} \ No newline at end of file diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/css_variables.css b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/css_variables.css new file mode 100644 index 000000000..48bb66a3e --- /dev/null +++ b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/css_variables.css @@ -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')} diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/css_variables.less b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/css_variables.less new file mode 100644 index 000000000..41b1dc48e --- /dev/null +++ b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/css_variables.less @@ -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"; diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/css_variables.source.less b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/css_variables.source.less new file mode 100644 index 000000000..28ed8727a --- /dev/null +++ b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/css_variables.source.less @@ -0,0 +1,5 @@ +@mycolor: #0000ff; + +:root { +--mycolor: @mycolor; +} diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library-RTL.css b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library-RTL.css new file mode 100644 index 000000000..5eac03f06 --- /dev/null +++ b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library-RTL.css @@ -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')} diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library-parameters.json b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library-parameters.json new file mode 100644 index 000000000..a0c491380 --- /dev/null +++ b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library-parameters.json @@ -0,0 +1 @@ +{"mycolor":"#00f"} \ No newline at end of file diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library.css b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library.css new file mode 100644 index 000000000..5eac03f06 --- /dev/null +++ b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library.css @@ -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')} diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library.less b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library.less new file mode 100644 index 000000000..f3fda6d3e --- /dev/null +++ b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library.less @@ -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; +} diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library.source.less b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library.source.less new file mode 100644 index 000000000..d864e666d --- /dev/null +++ b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library.source.less @@ -0,0 +1,9 @@ +/*! + * Some fancy copyright + */ + +@mycolor: blue; + +.sapUiBody { + background-color: @mycolor; +} diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library_skeleton-RTL.css b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library_skeleton-RTL.css new file mode 100644 index 000000000..654b3877e --- /dev/null +++ b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library_skeleton-RTL.css @@ -0,0 +1,3 @@ +/*! + * Some fancy copyright + */.sapUiBody{background-color:var(--mycolor)} \ No newline at end of file diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library_skeleton.css b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library_skeleton.css new file mode 100644 index 000000000..654b3877e --- /dev/null +++ b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library_skeleton.css @@ -0,0 +1,3 @@ +/*! + * Some fancy copyright + */.sapUiBody{background-color:var(--mycolor)} \ No newline at end of file diff --git a/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/test-resources/theme/library/e/Test.html b/test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/test-resources/theme/library/e/Test.html new file mode 100644 index 000000000..e69de29bb diff --git a/test/lib/builder/builder.js b/test/lib/builder/builder.js index 1561f4e80..6153b353b 100644 --- a/test/lib/builder/builder.js +++ b/test/lib/builder/builder.js @@ -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"; @@ -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"); diff --git a/test/lib/tasks/generateThemeDesignerResources.js b/test/lib/tasks/generateThemeDesignerResources.js index 5961ca6d4..3f4a20b14 100644 --- a/test/lib/tasks/generateThemeDesignerResources.js +++ b/test/lib/tasks/generateThemeDesignerResources.js @@ -409,6 +409,329 @@ test.serial("generateThemeDesignerResources: Theme-Library", async (t) => { "workspace.write should be called with libraryLessResource"); }); +test.serial("generateThemeDesignerResources: Theme-Library with CSS Variables", async (t) => { + const {generateThemeDesignerResources, libraryLessGeneratorStub, ResourceStub} = t.context; + + const librarySourceLessResource = { + getPath: sinon.stub().returns("/resources/sap/ui/demo/lib/themes/my_theme/library.source.less") + }; + + const cssVariablesSourceResource = { + getString: sinon.stub().returns("My Content"), + }; + + const cssVariableSourceLessResource = { + getPath: sinon.stub().returns("/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") + }; + + const workspace = { + byGlob: sinon.stub().callsFake(async (globPattern) => { + if (globPattern === "/resources/**/themes/*/library.source.less") { + return [librarySourceLessResource]; + } else if (globPattern === "/resources/**/themes/*/css_variables.source.less") { + return [cssVariableSourceLessResource]; + } else { + return []; + } + }), + byPath: sinon.stub().callsFake(async (virPath) => { + if (virPath === "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") { + return cssVariablesSourceResource; + } else { + return []; + } + }), + write: sinon.stub() + }; + const dependencies = {}; + + const libraryLessResource = {}; + + libraryLessGeneratorStub.resolves([libraryLessResource]); + + await generateThemeDesignerResources({ + workspace, + dependencies, + options: { + projectName: "sap.ui.demo.lib", + version: "1.2.3" + } + }); + + t.is(ResourceStub.callCount, 2); + t.true(ResourceStub.alwaysCalledWithNew()); + + t.deepEqual(ResourceStub.getCall(1).args, [{ + path: "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.less", + string: +`/* 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" */ +My Content +/* END "css_variables.source.less" */ + +@import "../../../../../../sap/ui/core/themes/my_theme/global.less"; +` + }]); + const cssVariableResource = ResourceStub.getCall(1).returnValue; + + t.is(workspace.write.callCount, 3); + t.is(workspace.write.getCall(2).args.length, 1, + "workspace.write for cssVariableResource should be called with 1 argument"); + t.is(workspace.write.getCall(2).args[0], cssVariableResource, + "workspace.write should be called with cssVariableResource"); +}); + +test.serial("generateThemeDesignerResources: Theme-Library with CSS Variables with namespace", async (t) => { + const {generateThemeDesignerResources, libraryLessGeneratorStub, ResourceStub} = t.context; + + const librarySourceLessResource = { + getPath: sinon.stub().returns("/resources/sap/ui/demo/lib/themes/my_theme/library.source.less") + }; + + const cssVariablesSourceResource = { + getString: sinon.stub().returns("My Content from Namespace"), + }; + + const cssVariableSourceLessResource = { + getPath: sinon.stub().returns("/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") + }; + + const workspace = { + byGlob: sinon.stub().callsFake(async (globPattern) => { + if (globPattern === "/resources/sap/ui/demo/lib/themes/*/library.source.less") { + return [librarySourceLessResource]; + } else if (globPattern === "/resources/sap/ui/demo/lib/themes/*/css_variables.source.less") { + return [cssVariableSourceLessResource]; + } else { + return []; + } + }), + byPath: sinon.stub().callsFake(async (virPath) => { + if (virPath === "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") { + return cssVariablesSourceResource; + } else { + return []; + } + }), + write: sinon.stub() + }; + const dependencies = {}; + + const libraryLessResource = {}; + + libraryLessGeneratorStub.resolves([libraryLessResource]); + + await generateThemeDesignerResources({ + workspace, + dependencies, + options: { + projectName: "sap.ui.demo.lib", + version: "1.2.3", + namespace: "sap/ui/demo/lib" + } + }); + + t.is(t.context.ReaderCollectionPrioritizedStub.callCount, 1, "ReaderCollectionPrioritized should be created once"); + t.deepEqual(t.context.ReaderCollectionPrioritizedStub.getCall(0).args, [{ + name: `generateThemeDesignerResources - prioritize workspace over dependencies: sap.ui.demo.lib`, + readers: [workspace, dependencies] + }]); + + t.is(ResourceStub.callCount, 3); + t.true(ResourceStub.alwaysCalledWithNew()); + + t.deepEqual(ResourceStub.getCall(2).args, [{ + path: "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.less", + string: +`/* 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" */ +My Content from Namespace +/* END "css_variables.source.less" */ + +@import "../../../../../../sap/ui/core/themes/my_theme/global.less"; +` + }]); + const cssVariableResource = ResourceStub.getCall(2).returnValue; + + t.is(workspace.write.callCount, 4); + t.is(workspace.write.getCall(3).args.length, 1, + "workspace.write for cssVariableResource should be called with 1 argument"); + t.is(workspace.write.getCall(3).args[0], cssVariableResource, + "workspace.write should be called with cssVariableResource"); +}); + +test.serial("generateThemeDesignerResources: Theme-Library with CSS Variables with base theme", async (t) => { + const { + generateThemeDesignerResources, + libraryLessGeneratorStub, + ResourceStub, + ReaderCollectionPrioritizedStub + } = t.context; + + const librarySourceLessResource = { + getPath: sinon.stub().returns("/resources/sap/ui/demo/lib/themes/my_theme/library.source.less") + }; + + const cssVariablesSourceResource = { + getString: sinon.stub().returns("My Content with Base Theme"), + }; + + const cssVariableSourceLessResource = { + getPath: sinon.stub().returns("/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") + }; + + const baseLessResource = {}; + + ReaderCollectionPrioritizedStub.returns({ + byPath: sinon.stub().callsFake(async (virPath) => { + if (virPath === "/resources/sap/ui/core/themes/my_theme/base.less") { + return baseLessResource; + } else { + return null; + } + }) + }); + + const workspace = { + byGlob: sinon.stub().callsFake(async (globPattern) => { + if (globPattern === "/resources/**/themes/*/library.source.less") { + return [librarySourceLessResource]; + } else if (globPattern === "/resources/**/themes/*/css_variables.source.less") { + return [cssVariableSourceLessResource]; + } else { + return []; + } + }), + byPath: sinon.stub().callsFake(async (virPath) => { + if (virPath === "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") { + return cssVariablesSourceResource; + } else { + return []; + } + }), + write: sinon.stub() + }; + const dependencies = {}; + + const libraryLessResource = {}; + + libraryLessGeneratorStub.resolves([libraryLessResource]); + + await generateThemeDesignerResources({ + workspace, + dependencies, + options: { + projectName: "sap.ui.demo.lib", + version: "1.2.3" + } + }); + + t.is(ResourceStub.callCount, 2); + t.true(ResourceStub.alwaysCalledWithNew()); + + t.deepEqual(ResourceStub.getCall(1).args, [{ + path: "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.less", + string: +`/* 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" */ +My Content with Base Theme +/* END "css_variables.source.less" */ + +@import "../../../../../../../Base/baseLib/my_theme/base.less"; +@import "../../../../../../sap/ui/core/themes/my_theme/global.less"; +` + }]); + const cssVariableResource = ResourceStub.getCall(1).returnValue; + + t.is(workspace.write.callCount, 3); + t.is(workspace.write.getCall(2).args.length, 1, + "workspace.write for cssVariableResource should be called with 1 argument"); + t.is(workspace.write.getCall(2).args[0], cssVariableResource, + "workspace.write should be called with cssVariableResource"); +}); + +test.serial("generateThemeDesignerResources: Base Theme-Library with CSS Variables", async (t) => { + const { + generateThemeDesignerResources, + libraryLessGeneratorStub, + ResourceStub + } = t.context; + + const librarySourceLessResource = { + getPath: sinon.stub().returns("/resources/sap/ui/demo/lib/themes/base/library.source.less") + }; + + const cssVariablesSourceResource = { + getString: sinon.stub().returns("My Base Theme Content"), + }; + + const cssVariableSourceLessResource = { + getPath: sinon.stub().returns("/resources/sap/ui/demo/lib/themes/base/css_variables.source.less") + }; + + const workspace = { + byGlob: sinon.stub().callsFake(async (globPattern) => { + if (globPattern === "/resources/**/themes/*/library.source.less") { + return [librarySourceLessResource]; + } else if (globPattern === "/resources/**/themes/*/css_variables.source.less") { + return [cssVariableSourceLessResource]; + } else { + return []; + } + }), + byPath: sinon.stub().callsFake(async (virPath) => { + if (virPath === "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") { + return cssVariablesSourceResource; + } else { + return []; + } + }), + write: sinon.stub() + }; + const dependencies = {}; + + const libraryLessResource = {}; + + libraryLessGeneratorStub.resolves([libraryLessResource]); + + await generateThemeDesignerResources({ + workspace, + dependencies, + options: { + projectName: "sap.ui.demo.lib", + version: "1.2.3" + } + }); + + t.is(ResourceStub.callCount, 2); + t.true(ResourceStub.alwaysCalledWithNew()); + + t.deepEqual(ResourceStub.getCall(1).args, [{ + path: "/resources/sap/ui/demo/lib/themes/base/css_variables.less", + string: +`/* NOTE: This file was generated as an optimized version of "css_variables.source.less" for the Theme Designer. */ + +@import "../../../../../../sap/ui/core/themes/base/global.less"; +` + }]); + const cssVariableResource = ResourceStub.getCall(1).returnValue; + + t.is(workspace.write.callCount, 3); + t.is(workspace.write.getCall(2).args.length, 1, + "workspace.write for cssVariableResource should be called with 1 argument"); + t.is(workspace.write.getCall(2).args[0], cssVariableResource, + "workspace.write should be called with cssVariableResource"); +}); + test.serial("generateThemeDesignerResources: .theming file missing in sap.ui.core library source`", async (t) => { const {generateThemeDesignerResources, libraryLessGeneratorStub, ResourceStub} = t.context;