From 375d1162d25adc715b2b0511681451daeeeebf75 Mon Sep 17 00:00:00 2001 From: Florian Vogt Date: Wed, 6 Apr 2022 14:53:35 +0200 Subject: [PATCH 1/7] [FEATURE] Enhance task generateThemeDesignerResources.js to create css_variables.less files --- lib/tasks/generateThemeDesignerResources.js | 110 ++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/lib/tasks/generateThemeDesignerResources.js b/lib/tasks/generateThemeDesignerResources.js index 94cf720b6..58753a75c 100644 --- a/lib/tasks/generateThemeDesignerResources.js +++ b/lib/tasks/generateThemeDesignerResources.js @@ -3,6 +3,30 @@ const log = require("@ui5/logger").getLogger("builder:tasks:generateThemeDesigne const libraryLessGenerator = require("../processors/libraryLessGenerator"); const {ReaderCollectionPrioritized, Resource, fsInterface} = require("@ui5/fs"); +const CSS_VARIABLES_SOURCE_LESS = "css_variables.source.less"; +const CSS_VARIABLES_LESS = "css_variables.less"; + +/** + * 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); +} + function generateLibraryDotTheming({namespace, version, hasThemes}) { const dotTheming = { sEntity: "Library", @@ -30,6 +54,61 @@ function generateLibraryDotTheming({namespace, version, hasThemes}) { }); } +function lessImport(filePath) { + return `@import "${filePath}";\n`; +} + +async function createCssVariablesLessResource({workspace, dependencies, themeFolder}) { + const pathToRoot = getPathToRoot(themeFolder); + const combo = new ReaderCollectionPrioritized({ + name: `createCssVariablesLessResource - prioritize workspace over dependencies:`, + readers: [workspace, dependencies] + }); + + // 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 "${CSS_VARIABLES_SOURCE_LESS}" \ +for the Theme Designer. */\n\n`; + + if (themeName !== "base") { + const cssVariablesSourceLessResource = await workspace.byPath( + posixPath.join(themeFolder, CSS_VARIABLES_SOURCE_LESS) + ); + + if (!cssVariablesSourceLessResource) { + throw new Error(`Could not find file "${CSS_VARIABLES_SOURCE_LESS}" in theme "${themeFolder}"`); + } + + const cssVariablesSourceLess = await cssVariablesSourceLessResource.getString(); + + cssVariablesLess += lessImport(`../base/${CSS_VARIABLES_LESS}`); + cssVariablesLess += ` +/* START "${CSS_VARIABLES_SOURCE_LESS}" */ +${cssVariablesSourceLess} +/* END "${CSS_VARIABLES_SOURCE_LESS}" */ + +`; + } + + if (hasBaseLess) { + cssVariablesLess += lessImport(`${pathToRoot}../Base/baseLib/${baseLibThemeName}/base.less`); + } + cssVariablesLess += lessImport(`${pathToRoot}sap/ui/core/themes/${themeName}/global.less`); + + const cssVariablesLessResource = new Resource({ + path: posixPath.join(themeFolder, CSS_VARIABLES_LESS), + string: cssVariablesLess + }); + await workspace.write(cssVariablesLessResource); +} + async function generateThemeDotTheming({workspace, combo, themeFolder}) { const themeName = posixPath.basename(themeFolder); const libraryMatchPattern = /^\/resources\/(.*)\/themes\/[^/]*$/i; @@ -163,4 +242,35 @@ module.exports = async function({workspace, dependencies, options: {projectName, await Promise.all( libraryLessResources.map((resource) => workspace.write(resource)) ); + + 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) { + // css_variables.less + const cssVariablesLessResource = cssVariablesSourceLessResource.map((cssVariableSourceLess) => { + const themeFolder = posixPath.dirname(cssVariableSourceLess.getPath()); + log.verbose(`Generatingc css_variables.less for theme ${themeFolder}`); + return createCssVariablesLessResource({ + workspace, dependencies, themeFolder + }); + }); + await Promise.all( + cssVariablesLessResource.map(async (resource) => { + if (resource) { + await workspace.write(resource); + } + }) + ); + } }; From 2fbfecf07e7bbd0240afe9711c232ac161f3d90b Mon Sep 17 00:00:00 2001 From: Florian Vogt Date: Fri, 8 Apr 2022 09:04:57 +0200 Subject: [PATCH 2/7] Add integration tests --- lib/tasks/generateThemeDesignerResources.js | 17 ++++++---------- .../theme/library/e/themes/my_theme/.theme | 9 +++++++++ .../theme/library/e/themes/my_theme/.theming | 6 ++++++ .../e/themes/my_theme/css_variables.css | 3 +++ .../e/themes/my_theme/css_variables.less | 14 +++++++++++++ .../themes/my_theme/css_variables.source.less | 5 +++++ .../library/e/themes/my_theme/library-RTL.css | 5 +++++ .../e/themes/my_theme/library-parameters.json | 1 + .../library/e/themes/my_theme/library.css | 5 +++++ .../library/e/themes/my_theme/library.less | 11 ++++++++++ .../e/themes/my_theme/library.source.less | 9 +++++++++ .../themes/my_theme/library_skeleton-RTL.css | 3 +++ .../e/themes/my_theme/library_skeleton.css | 3 +++ .../test-resources/theme/library/e/Test.html | 0 test/lib/builder/builder.js | 20 +++++++++++++++++++ 15 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/.theme create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/.theming create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/css_variables.css create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/css_variables.less create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/css_variables.source.less create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library-RTL.css create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library-parameters.json create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library.css create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library.less create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library.source.less create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library_skeleton-RTL.css create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/resources/theme/library/e/themes/my_theme/library_skeleton.css create mode 100644 test/expected/build/theme.library.e/dest-css-variables-theme-designer-resources/test-resources/theme/library/e/Test.html diff --git a/lib/tasks/generateThemeDesignerResources.js b/lib/tasks/generateThemeDesignerResources.js index 58753a75c..36a9de853 100644 --- a/lib/tasks/generateThemeDesignerResources.js +++ b/lib/tasks/generateThemeDesignerResources.js @@ -258,18 +258,13 @@ module.exports = async function({workspace, dependencies, options: {projectName, if (hasCssVariables) { // css_variables.less - const cssVariablesLessResource = cssVariablesSourceLessResource.map((cssVariableSourceLess) => { - const themeFolder = posixPath.dirname(cssVariableSourceLess.getPath()); - log.verbose(`Generatingc css_variables.less for theme ${themeFolder}`); - return createCssVariablesLessResource({ - workspace, dependencies, themeFolder - }); - }); await Promise.all( - cssVariablesLessResource.map(async (resource) => { - if (resource) { - await workspace.write(resource); - } + cssVariablesSourceLessResource.map((cssVariableSourceLess) => { + const themeFolder = posixPath.dirname(cssVariableSourceLess.getPath()); + log.verbose(`Generating css_variables.less for theme ${themeFolder}`); + return createCssVariablesLessResource({ + workspace, dependencies, themeFolder + }); }) ); } 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..f8f5a0c8c 100644 --- a/test/lib/builder/builder.js +++ b/test/lib/builder/builder.js @@ -971,6 +971,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"); From e61c92b6acc1b5723230d6a82921e7f75b7a1732 Mon Sep 17 00:00:00 2001 From: Florian Vogt Date: Fri, 8 Apr 2022 11:12:10 +0200 Subject: [PATCH 3/7] refactor code and add unit test (wip) --- lib/tasks/generateThemeDesignerResources.js | 88 ++++++++++--------- .../tasks/generateThemeDesignerResources.js | 75 ++++++++++++++++ 2 files changed, 121 insertions(+), 42 deletions(-) diff --git a/lib/tasks/generateThemeDesignerResources.js b/lib/tasks/generateThemeDesignerResources.js index 36a9de853..720d73829 100644 --- a/lib/tasks/generateThemeDesignerResources.js +++ b/lib/tasks/generateThemeDesignerResources.js @@ -3,9 +3,6 @@ const log = require("@ui5/logger").getLogger("builder:tasks:generateThemeDesigne const libraryLessGenerator = require("../processors/libraryLessGenerator"); const {ReaderCollectionPrioritized, Resource, fsInterface} = require("@ui5/fs"); -const CSS_VARIABLES_SOURCE_LESS = "css_variables.source.less"; -const CSS_VARIABLES_LESS = "css_variables.less"; - /** * Returns a relative path from the given themeFolder to the root namespace. * @@ -58,12 +55,10 @@ function lessImport(filePath) { return `@import "${filePath}";\n`; } -async function createCssVariablesLessResource({workspace, dependencies, themeFolder}) { +async function createCssVariablesLessResource({workspace, combo, themeFolder}) { const pathToRoot = getPathToRoot(themeFolder); - const combo = new ReaderCollectionPrioritized({ - name: `createCssVariablesLessResource - prioritize workspace over dependencies:`, - readers: [workspace, dependencies] - }); + 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); @@ -74,25 +69,25 @@ async function createCssVariablesLessResource({workspace, dependencies, themeFol 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 "${CSS_VARIABLES_SOURCE_LESS}" \ +`/* 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, CSS_VARIABLES_SOURCE_LESS) + posixPath.join(themeFolder, cssVariablesSourceLessFile) ); if (!cssVariablesSourceLessResource) { - throw new Error(`Could not find file "${CSS_VARIABLES_SOURCE_LESS}" in theme "${themeFolder}"`); + throw new Error(`Could not find file "${cssVariablesSourceLessFile}" in theme "${themeFolder}"`); } const cssVariablesSourceLess = await cssVariablesSourceLessResource.getString(); - cssVariablesLess += lessImport(`../base/${CSS_VARIABLES_LESS}`); + cssVariablesLess += lessImport(`../base/${cssVariablesLessFile}`); cssVariablesLess += ` -/* START "${CSS_VARIABLES_SOURCE_LESS}" */ +/* START "${cssVariablesSourceLessFile}" */ ${cssVariablesSourceLess} -/* END "${CSS_VARIABLES_SOURCE_LESS}" */ +/* END "${cssVariablesSourceLessFile}" */ `; } @@ -102,11 +97,10 @@ ${cssVariablesSourceLess} } cssVariablesLess += lessImport(`${pathToRoot}sap/ui/core/themes/${themeName}/global.less`); - const cssVariablesLessResource = new Resource({ - path: posixPath.join(themeFolder, CSS_VARIABLES_LESS), + return new Resource({ + path: posixPath.join(themeFolder, cssVariablesLessFile), string: cssVariablesLess }); - await workspace.write(cssVariablesLessResource); } async function generateThemeDotTheming({workspace, combo, themeFolder}) { @@ -159,6 +153,39 @@ async function generateThemeDotTheming({workspace, combo, themeFolder}) { return newDotThemingResource; } +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) { + const cssVariablesLessResources = await Promise.all( + cssVariablesSourceLessResource.map((cssVariableSourceLess) => { + const themeFolder = posixPath.dirname(cssVariableSourceLess.getPath()); + log.verbose(`Generating css_variables.less for theme ${themeFolder}`); + return createCssVariablesLessResource({ + workspace, combo, themeFolder + }); + }) + ); + + return cssVariablesLessResources.map(async (resource) => { + if (resource) { + await workspace.write(resource); + } + }); + } +} + /** * Generates resources required for integration with the SAP Theme Designer. * @@ -243,29 +270,6 @@ module.exports = async function({workspace, dependencies, options: {projectName, libraryLessResources.map((resource) => workspace.write(resource)) ); - 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) { - // css_variables.less - await Promise.all( - cssVariablesSourceLessResource.map((cssVariableSourceLess) => { - const themeFolder = posixPath.dirname(cssVariableSourceLess.getPath()); - log.verbose(`Generating css_variables.less for theme ${themeFolder}`); - return createCssVariablesLessResource({ - workspace, dependencies, themeFolder - }); - }) - ); - } + // css_variables.less + await generateCssVariablesLess({workspace, combo, namespace}); }; diff --git a/test/lib/tasks/generateThemeDesignerResources.js b/test/lib/tasks/generateThemeDesignerResources.js index 5961ca6d4..168a7e27a 100644 --- a/test/lib/tasks/generateThemeDesignerResources.js +++ b/test/lib/tasks/generateThemeDesignerResources.js @@ -409,6 +409,81 @@ 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, fsInterfaceStub, ResourceStub} = t.context; + + const librarySourceLessResource = { + getPath: sinon.stub().returns("/resources/sap/ui/demo/lib/themes/my_theme/library.source.less") + }; + + const workspace = { + byGlob: sinon.stub().callsFake(async (globPattern) => { + if (globPattern === "/resources/**/themes/*/library.source.less") { + return [librarySourceLessResource]; + } 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(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] + }]); + const combo = t.context.ReaderCollectionPrioritizedStub.getCall(0).returnValue; + + t.is(fsInterfaceStub.callCount, 1, "fsInterface should be created once"); + t.deepEqual(fsInterfaceStub.getCall(0).args, [combo], "fsInterface should be created for 'combo'"); + const fs = fsInterfaceStub.getCall(0).returnValue; + + t.is(libraryLessGeneratorStub.callCount, 1); + + t.deepEqual(libraryLessGeneratorStub.getCall(0).args[0], { + resources: [librarySourceLessResource], + fs, + }, "libraryLessGenerator processor should be called with expected arguments"); + + t.is(ResourceStub.callCount, 1); + t.true(ResourceStub.alwaysCalledWithNew()); + + t.deepEqual(ResourceStub.getCall(0).args, [{ + path: "/resources/sap/ui/demo/lib/themes/my_theme/.theming", + string: JSON.stringify({ + sEntity: "Theme", + sId: "my_theme", + sVendor: "SAP", + oExtends: "base" + }, null, 2) + }]); + const myThemeDotTheming = ResourceStub.getCall(0).returnValue; + + t.is(workspace.write.callCount, 2); + t.is(workspace.write.getCall(0).args.length, 1, + "workspace.write for myThemeDotTheming should be called with 1 argument"); + t.is(workspace.write.getCall(0).args[0], myThemeDotTheming, + "workspace.write should be called with myThemeDotTheming"); + t.is(workspace.write.getCall(1).args.length, 1, + "workspace.write for libraryLessResource should be called with 1 argument"); + t.is(workspace.write.getCall(1).args[0], libraryLessResource, + "workspace.write should be called with libraryLessResource"); +}); + test.serial("generateThemeDesignerResources: .theming file missing in sap.ui.core library source`", async (t) => { const {generateThemeDesignerResources, libraryLessGeneratorStub, ResourceStub} = t.context; From 96abce15243513751e2f80c757f4ee6c5f1ab997 Mon Sep 17 00:00:00 2001 From: Florian Vogt Date: Fri, 8 Apr 2022 19:38:22 +0200 Subject: [PATCH 4/7] Add test --- .../tasks/generateThemeDesignerResources.js | 216 +++++++++++++++--- 1 file changed, 187 insertions(+), 29 deletions(-) diff --git a/test/lib/tasks/generateThemeDesignerResources.js b/test/lib/tasks/generateThemeDesignerResources.js index 168a7e27a..da76c2b3d 100644 --- a/test/lib/tasks/generateThemeDesignerResources.js +++ b/test/lib/tasks/generateThemeDesignerResources.js @@ -410,16 +410,33 @@ test.serial("generateThemeDesignerResources: Theme-Library", async (t) => { }); test.serial("generateThemeDesignerResources: Theme-Library with CSS Variables", async (t) => { - const {generateThemeDesignerResources, libraryLessGeneratorStub, fsInterfaceStub, ResourceStub} = t.context; + 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 (globPattern) => { + if (globPattern === "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") { + return cssVariablesSourceResource; } else { return []; } @@ -441,47 +458,188 @@ test.serial("generateThemeDesignerResources: Theme-Library with CSS Variables", } }); + 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 (globPattern) => { + if (globPattern === "/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] }]); - const combo = t.context.ReaderCollectionPrioritizedStub.getCall(0).returnValue; - t.is(fsInterfaceStub.callCount, 1, "fsInterface should be created once"); - t.deepEqual(fsInterfaceStub.getCall(0).args, [combo], "fsInterface should be created for 'combo'"); - const fs = fsInterfaceStub.getCall(0).returnValue; + t.is(ResourceStub.callCount, 3); + t.true(ResourceStub.alwaysCalledWithNew()); - t.is(libraryLessGeneratorStub.callCount, 1); + 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. */ - t.deepEqual(libraryLessGeneratorStub.getCall(0).args[0], { - resources: [librarySourceLessResource], - fs, - }, "libraryLessGenerator processor should be called with expected arguments"); +@import "../base/css_variables.less"; - t.is(ResourceStub.callCount, 1); +/* 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) => { + // TODO: continue test + 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 (globPattern) => { + if (globPattern === "/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(0).args, [{ - path: "/resources/sap/ui/demo/lib/themes/my_theme/.theming", - string: JSON.stringify({ - sEntity: "Theme", - sId: "my_theme", - sVendor: "SAP", - oExtends: "base" - }, null, 2) + 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 myThemeDotTheming = ResourceStub.getCall(0).returnValue; + const cssVariableResource = ResourceStub.getCall(1).returnValue; - t.is(workspace.write.callCount, 2); - t.is(workspace.write.getCall(0).args.length, 1, - "workspace.write for myThemeDotTheming should be called with 1 argument"); - t.is(workspace.write.getCall(0).args[0], myThemeDotTheming, - "workspace.write should be called with myThemeDotTheming"); - t.is(workspace.write.getCall(1).args.length, 1, - "workspace.write for libraryLessResource should be called with 1 argument"); - t.is(workspace.write.getCall(1).args[0], libraryLessResource, - "workspace.write should be called with libraryLessResource"); + 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) => { From 24c1ee30645e39c530d02274b1833c8ab6a9da66 Mon Sep 17 00:00:00 2001 From: Florian Vogt Date: Tue, 12 Apr 2022 10:08:27 +0200 Subject: [PATCH 5/7] add tests for base themes --- .../tasks/generateThemeDesignerResources.js | 110 ++++++++++++++++-- 1 file changed, 100 insertions(+), 10 deletions(-) diff --git a/test/lib/tasks/generateThemeDesignerResources.js b/test/lib/tasks/generateThemeDesignerResources.js index da76c2b3d..3f4a20b14 100644 --- a/test/lib/tasks/generateThemeDesignerResources.js +++ b/test/lib/tasks/generateThemeDesignerResources.js @@ -434,8 +434,8 @@ test.serial("generateThemeDesignerResources: Theme-Library with CSS Variables", return []; } }), - byPath: sinon.stub().callsFake(async (globPattern) => { - if (globPattern === "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") { + byPath: sinon.stub().callsFake(async (virPath) => { + if (virPath === "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") { return cssVariablesSourceResource; } else { return []; @@ -509,8 +509,8 @@ test.serial("generateThemeDesignerResources: Theme-Library with CSS Variables wi return []; } }), - byPath: sinon.stub().callsFake(async (globPattern) => { - if (globPattern === "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") { + byPath: sinon.stub().callsFake(async (virPath) => { + if (virPath === "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") { return cssVariablesSourceResource; } else { return []; @@ -567,21 +567,37 @@ My Content from Namespace }); test.serial("generateThemeDesignerResources: Theme-Library with CSS Variables with base theme", async (t) => { - // TODO: continue test - const {generateThemeDesignerResources, libraryLessGeneratorStub, ResourceStub} = t.context; + 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"), + 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") { @@ -592,8 +608,8 @@ test.serial("generateThemeDesignerResources: Theme-Library with CSS Variables wi return []; } }), - byPath: sinon.stub().callsFake(async (globPattern) => { - if (globPattern === "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") { + byPath: sinon.stub().callsFake(async (virPath) => { + if (virPath === "/resources/sap/ui/demo/lib/themes/my_theme/css_variables.source.less") { return cssVariablesSourceResource; } else { return []; @@ -627,9 +643,10 @@ test.serial("generateThemeDesignerResources: Theme-Library with CSS Variables wi @import "../base/css_variables.less"; /* START "css_variables.source.less" */ -My Content +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"; ` }]); @@ -642,6 +659,79 @@ My Content "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; From 2a585e620b3fe17c7d61e8631c3b0b004fdbf83c Mon Sep 17 00:00:00 2001 From: Florian Vogt Date: Tue, 12 Apr 2022 10:34:42 +0200 Subject: [PATCH 6/7] Add integration test for library with theme and theme designer resources --- .../resources/theme/j/.theming | 5 +++++ .../theme/j/themes/somefancytheme/.theming | 6 +++++ .../theme/j/themes/somefancytheme/Button.less | 3 +++ .../j/themes/somefancytheme/css_variables.css | 3 +++ .../themes/somefancytheme/css_variables.less | 14 ++++++++++++ .../somefancytheme/css_variables.source.less | 5 +++++ .../j/themes/somefancytheme/library-RTL.css | 3 +++ .../somefancytheme/library-parameters.json | 1 + .../theme/j/themes/somefancytheme/library.css | 3 +++ .../j/themes/somefancytheme/library.less | 10 +++++++++ .../themes/somefancytheme/library.source.less | 2 ++ .../somefancytheme/library_skeleton-RTL.css | 1 + .../somefancytheme/library_skeleton.css | 1 + test/lib/builder/builder.js | 22 ++++++++++++++++++- 14 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/.theming create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/.theming create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/Button.less create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/css_variables.css create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/css_variables.less create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/css_variables.source.less create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library-RTL.css create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library-parameters.json create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library.css create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library.less create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library.source.less create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library_skeleton-RTL.css create mode 100644 test/expected/build/theme.j/dest-css-variables-theme-designer-resources/resources/theme/j/themes/somefancytheme/library_skeleton.css 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/lib/builder/builder.js b/test/lib/builder/builder.js index f8f5a0c8c..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,7 +991,7 @@ test.serial("Build theme-library with CSS variables", (t) => { }); }); -test.serial("Build theme-library with CSS variables and Theme Designer Resources", (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({ From a78f75318d87b32b16e4252fed587c7c8df6565f Mon Sep 17 00:00:00 2001 From: Florian Vogt Date: Wed, 13 Apr 2022 07:20:37 +0200 Subject: [PATCH 7/7] Refactor workspace write to loop only once over the resources --- lib/tasks/generateThemeDesignerResources.js | 123 ++++++++++---------- 1 file changed, 62 insertions(+), 61 deletions(-) diff --git a/lib/tasks/generateThemeDesignerResources.js b/lib/tasks/generateThemeDesignerResources.js index 720d73829..f8f75c4fa 100644 --- a/lib/tasks/generateThemeDesignerResources.js +++ b/lib/tasks/generateThemeDesignerResources.js @@ -24,6 +24,16 @@ function getPathToRoot(themeFolder) { 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", @@ -51,58 +61,6 @@ function generateLibraryDotTheming({namespace, version, hasThemes}) { }); } -function lessImport(filePath) { - return `@import "${filePath}";\n`; -} - -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 generateThemeDotTheming({workspace, combo, themeFolder}) { const themeName = posixPath.basename(themeFolder); const libraryMatchPattern = /^\/resources\/(.*)\/themes\/[^/]*$/i; @@ -153,6 +111,54 @@ 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) { @@ -168,21 +174,16 @@ async function generateCssVariablesLess({workspace, combo, namespace}) { const hasCssVariables = cssVariablesSourceLessResource.length > 0; if (hasCssVariables) { - const cssVariablesLessResources = await Promise.all( - cssVariablesSourceLessResource.map((cssVariableSourceLess) => { + await Promise.all( + cssVariablesSourceLessResource.map(async (cssVariableSourceLess) => { const themeFolder = posixPath.dirname(cssVariableSourceLess.getPath()); log.verbose(`Generating css_variables.less for theme ${themeFolder}`); - return createCssVariablesLessResource({ + const r = await createCssVariablesLessResource({ workspace, combo, themeFolder }); + return await workspace.write(r); }) ); - - return cssVariablesLessResources.map(async (resource) => { - if (resource) { - await workspace.write(resource); - } - }); } }