From 84d1cc8980de00459e2487dfc9077a4f9b2e70fe Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Thu, 18 Jul 2019 09:04:43 +0200 Subject: [PATCH 1/3] [FEATURE] Theme Build: Add compress option to minify output --- lib/processors/themeBuilder.js | 16 ++++++++++++---- lib/tasks/buildThemes.js | 5 ++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/processors/themeBuilder.js b/lib/processors/themeBuilder.js index 4a5d1999b..5df68ab40 100644 --- a/lib/processors/themeBuilder.js +++ b/lib/processors/themeBuilder.js @@ -28,9 +28,11 @@ class ThemeBuilder { * * @public * @param {module:@ui5/fs.Resource[]} resources Library files + * @param {Object} [options] Build options + * @param {boolean} [options.compress=false] Compress build output (CSS / JSON) * @returns {Promise} Resolving with array of created files */ - build(resources) { + build(resources, {compress = false} = {}) { const files = []; const compile = (resource) => { @@ -46,6 +48,9 @@ class ThemeBuilder { lessInputPath: resource.getPath(), library: { name: libraryName + }, + compiler: { + compress } }).then((result) => { const themeDir = path.dirname(resource.getPath()); @@ -62,7 +67,7 @@ class ThemeBuilder { const libParams = new Resource({ path: themeDir + "/library-parameters.json", - string: JSON.stringify(result.variables, null, "\t") + string: JSON.stringify(result.variables, null, compress ? null : "\t") }); files.push(libCss, libCssRtl, libParams); @@ -93,11 +98,14 @@ class ThemeBuilder { * @param {Object} parameters Parameters * @param {module:@ui5/fs.Resource[]} parameters.resources List of library.source.less resources to be processed * @param {fs|module:@ui5/fs.fsInterface} parameters.fs Node fs or custom [fs interface]{@link module:resources/module:@ui5/fs.fsInterface} + * @param {Object} [parameters.options] Options + * @param {Object} [parameters.options.compress=false] Compress build output (CSS / JSON) * @returns {Promise} Promise resolving with theme resources */ -module.exports = ({resources, fs}) => { +module.exports = ({resources, fs, options}) => { const themeBuilder = new ThemeBuilder({fs}); - return themeBuilder.build(resources).then((files) => { + const compress = options && options.compress; + return themeBuilder.build(resources, {compress}).then((files) => { themeBuilder.clearCache(); return files; }); diff --git a/lib/tasks/buildThemes.js b/lib/tasks/buildThemes.js index dad1dc271..0dd03688c 100644 --- a/lib/tasks/buildThemes.js +++ b/lib/tasks/buildThemes.js @@ -53,7 +53,10 @@ module.exports = function({workspace, dependencies, options}) { }).then((resources) => { return themeBuilder({ resources, - fs: fsInterface(combo) + fs: fsInterface(combo), + options: { + compress: true + } }); }).then((processedResources) => { return Promise.all(processedResources.map((resource) => { From cb5132258bba2cc2bed8b9163c7bb2aec88b58f0 Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Tue, 23 Jul 2019 16:34:34 +0200 Subject: [PATCH 2/3] [INTERNAL] Update Theme Build Tests --- .../theme/j/themes/somefancytheme/Button.less | 4 ++- .../j/themes/somefancytheme/library-RTL.css | 7 ++-- .../somefancytheme/library-parameters.json | 2 +- .../theme/j/themes/somefancytheme/library.css | 7 ++-- .../themes/somefancytheme/library.source.less | 1 + .../theme/j/themes/somefancytheme/Button.less | 4 ++- .../themes/somefancytheme/library.source.less | 1 + test/lib/tasks/buildThemes.js | 32 ++++--------------- 8 files changed, 19 insertions(+), 39 deletions(-) diff --git a/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/Button.less b/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/Button.less index 172060f90..ca968183f 100644 --- a/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/Button.less +++ b/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/Button.less @@ -1 +1,3 @@ -.someClass{color:black} +.someClass { + color: @someColor +} diff --git a/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library-RTL.css b/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library-RTL.css index 3824292b9..5009ca50e 100644 --- a/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library-RTL.css +++ b/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library-RTL.css @@ -1,6 +1,3 @@ -.someClass { - color: #000000; -} - +.someClass{color:#000} /* Inline theming parameters */ -#sap-ui-theme-theme\.j{background-image:url('data:text/plain;utf-8,%7B%7D')} +#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/resources/theme/j/themes/somefancytheme/library-parameters.json b/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library-parameters.json index 9e26dfeeb..a190cda03 100644 --- a/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library-parameters.json +++ b/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library-parameters.json @@ -1 +1 @@ -{} \ No newline at end of file +{"someColor":"#000"} \ No newline at end of file diff --git a/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library.css b/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library.css index 3824292b9..5009ca50e 100644 --- a/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library.css +++ b/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library.css @@ -1,6 +1,3 @@ -.someClass { - color: #000000; -} - +.someClass{color:#000} /* Inline theming parameters */ -#sap-ui-theme-theme\.j{background-image:url('data:text/plain;utf-8,%7B%7D')} +#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/resources/theme/j/themes/somefancytheme/library.source.less b/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library.source.less index 3092239b2..834de919e 100644 --- a/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library.source.less +++ b/test/expected/build/theme.j/dest/resources/theme/j/themes/somefancytheme/library.source.less @@ -1 +1,2 @@ +@someColor: black; @import "Button.less"; diff --git a/test/fixtures/theme.j/main/src/theme/j/themes/somefancytheme/Button.less b/test/fixtures/theme.j/main/src/theme/j/themes/somefancytheme/Button.less index 172060f90..ca968183f 100644 --- a/test/fixtures/theme.j/main/src/theme/j/themes/somefancytheme/Button.less +++ b/test/fixtures/theme.j/main/src/theme/j/themes/somefancytheme/Button.less @@ -1 +1,3 @@ -.someClass{color:black} +.someClass { + color: @someColor +} diff --git a/test/fixtures/theme.j/main/src/theme/j/themes/somefancytheme/library.source.less b/test/fixtures/theme.j/main/src/theme/j/themes/somefancytheme/library.source.less index 3092239b2..834de919e 100644 --- a/test/fixtures/theme.j/main/src/theme/j/themes/somefancytheme/library.source.less +++ b/test/fixtures/theme.j/main/src/theme/j/themes/somefancytheme/library.source.less @@ -1 +1,2 @@ +@someColor: black; @import "Button.less"; diff --git a/test/lib/tasks/buildThemes.js b/test/lib/tasks/buildThemes.js index 4b6aef28e..91df4647b 100644 --- a/test/lib/tasks/buildThemes.js +++ b/test/lib/tasks/buildThemes.js @@ -25,27 +25,17 @@ test("integration: simple", (t) => { padding: 1px 2px 3px 4px; }`; const cssExpected = -`.fluffyHammer { - color: #123456; - padding: 1px 2px 3px 4px; -} - +`.fluffyHammer{color:#123456;padding:1px 2px 3px 4px} /* Inline theming parameters */ #sap-ui-theme-super\\.duper\\.looper{background-image:url('data:text/plain;utf-8,%7B%22deepSea%22%3A%22%23123456%22%7D')} `; const cssRtlExpected = -`.fluffyHammer { - color: #123456; - padding: 1px 4px 3px 2px; -} - +`.fluffyHammer{color:#123456;padding:1px 4px 3px 2px} /* Inline theming parameters */ #sap-ui-theme-super\\.duper\\.looper{background-image:url('data:text/plain;utf-8,%7B%22deepSea%22%3A%22%23123456%22%7D')} `; const parametersExpected = -`{ - "deepSea": "#123456" -}`; +`{"deepSea":"#123456"}`; const lessPath = "/resources/super/duper/looper/themes/brightlight/library.source.less"; const cssPath = "/resources/super/duper/looper/themes/brightlight/library.css"; const cssRtlPath = "/resources/super/duper/looper/themes/brightlight/library-RTL.css"; @@ -106,27 +96,17 @@ test("integration: imports", (t) => { const lessVariablesContent = "@deepSea: #123456;"; const cssExpected = -`.fluffyHammer { - color: #123456; - padding: 1px 2px 3px 4px; -} - +`.fluffyHammer{color:#123456;padding:1px 2px 3px 4px} /* Inline theming parameters */ #sap-ui-theme-super\\.duper\\.looper{background-image:url('data:text/plain;utf-8,%7B%22deepSea%22%3A%22%23123456%22%7D')} `; const cssRtlExpected = -`.fluffyHammer { - color: #123456; - padding: 1px 4px 3px 2px; -} - +`.fluffyHammer{color:#123456;padding:1px 4px 3px 2px} /* Inline theming parameters */ #sap-ui-theme-super\\.duper\\.looper{background-image:url('data:text/plain;utf-8,%7B%22deepSea%22%3A%22%23123456%22%7D')} `; const parametersExpected = -`{ - "deepSea": "#123456" -}`; +`{"deepSea":"#123456"}`; const lessPath = "/resources/super/duper/looper/themes/brightlight/library.source.less"; const lessVariablesPath = "/resources/super/duper/looper/themes/brightlight/variables.less"; const cssPath = "/resources/super/duper/looper/themes/brightlight/library.css"; From a00a34999e1524f917b75d7428b09fa33b32a25e Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Thu, 1 Aug 2019 11:20:35 +0200 Subject: [PATCH 3/3] [INTERNAL] Add themeBuilder processor test --- test/lib/processors/themeBuilder.js | 208 ++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 test/lib/processors/themeBuilder.js diff --git a/test/lib/processors/themeBuilder.js b/test/lib/processors/themeBuilder.js new file mode 100644 index 000000000..c20ef1a34 --- /dev/null +++ b/test/lib/processors/themeBuilder.js @@ -0,0 +1,208 @@ +const test = require("ava"); + +const resourceFactory = require("@ui5/fs").resourceFactory; +const fsInterface = require("@ui5/fs").fsInterface; + +const themeBuilderProcessor = require("../../../lib/processors/themeBuilder"); +const ThemeBuilder = require("../../../lib/processors/themeBuilder").ThemeBuilder; + +function prepareResources({library} = {}) { + const input = +`@someColor: black; +.someClass { + color: @someColor; + padding: 1px 2px 3px 4px; +}`; + + const memoryAdapter = resourceFactory.createAdapter({ + virBasePath: "/" + }); + + let lessFilePath; + if (library === false) { + lessFilePath = "/resources/foo.less"; + } else { + lessFilePath = "/resources/sap/ui/foo/themes/base/library.source.less"; + } + + const resource = resourceFactory.createResource({ + path: lessFilePath, + string: input + }); + + memoryAdapter.write(resource); + + return { + resource, + memoryAdapter + }; +} + +function getExpectedResults({compress, library}) { + let css; let cssRtl; let json; + if (compress) { + css = +`.someClass{color:#000;padding:1px 2px 3px 4px}`; + + cssRtl = +`.someClass{color:#000;padding:1px 4px 3px 2px}`; + json = `{"someColor":"#000"}`; + } else { + css = +`.someClass { + color: #000000; + padding: 1px 2px 3px 4px; +} +`; + + cssRtl = +`.someClass { + color: #000000; + padding: 1px 4px 3px 2px; +} +`; + + json = +`{ + "someColor": "#000000" +}`; + } + + if (library !== false) { + css += +` +/* Inline theming parameters */ +#sap-ui-theme-sap\\.ui\\.foo{background-image:url('data:text/plain;utf-8,%7B%22someColor%22%3A%22%23${compress ? "000" : "000000"}%22%7D')} +`; + cssRtl += +` +/* Inline theming parameters */ +#sap-ui-theme-sap\\.ui\\.foo{background-image:url('data:text/plain;utf-8,%7B%22someColor%22%3A%22%23${compress ? "000" : "000000"}%22%7D')} +`; + } + + return {css, cssRtl, json}; +} + +test("Processor: Builds a less file (default options)", async (t) => { + const {resource, memoryAdapter} = prepareResources(); + + const [cssResource, cssRtlResource, jsonResource] = await themeBuilderProcessor({ + resources: [resource], + fs: fsInterface(memoryAdapter) + }); + + const expected = getExpectedResults({compress: false}); + t.is(await cssResource.getString(), expected.css, "CSS should be correct"); + t.is(await cssRtlResource.getString(), expected.cssRtl, "Right-to-left CSS should be correct"); + t.is(await jsonResource.getString(), expected.json, "JSON should be correct"); +}); + +test("Processor: Builds a less file (compress = true)", async (t) => { + const {resource, memoryAdapter} = prepareResources(); + + const [cssResource, cssRtlResource, jsonResource] = await themeBuilderProcessor({ + resources: [resource], + fs: fsInterface(memoryAdapter), + options: { + compress: true + } + }); + + const expected = getExpectedResults({compress: true}); + t.is(await cssResource.getString(), expected.css, "CSS should be correct"); + t.is(await cssRtlResource.getString(), expected.cssRtl, "Right-to-left CSS should be correct"); + t.is(await jsonResource.getString(), expected.json, "JSON should be correct"); +}); + +test("Processor: Builds a less file (compress = false)", async (t) => { + const {resource, memoryAdapter} = prepareResources(); + + const [cssResource, cssRtlResource, jsonResource] = await themeBuilderProcessor({ + resources: [resource], + fs: fsInterface(memoryAdapter), + options: { + compress: false + } + }); + + const expected = getExpectedResults({compress: false}); + t.is(await cssResource.getString(), expected.css, "CSS should be correct"); + t.is(await cssRtlResource.getString(), expected.cssRtl, "Right-to-left CSS should be correct"); + t.is(await jsonResource.getString(), expected.json, "JSON should be correct"); +}); + +test("Processor: Builds a less file (no library)", async (t) => { + const {resource, memoryAdapter} = prepareResources({library: false}); + + const [cssResource, cssRtlResource, jsonResource] = await themeBuilderProcessor({ + resources: [resource], + fs: fsInterface(memoryAdapter), + options: { + compress: false + } + }); + + const expected = getExpectedResults({compress: false, library: false}); + t.is(await cssResource.getString(), expected.css, "CSS should be correct"); + t.is(await cssRtlResource.getString(), expected.cssRtl, "Right-to-left CSS should be correct"); + t.is(await jsonResource.getString(), expected.json, "JSON should be correct"); +}); + +test("ThemeBuilder: Builds a less file", async (t) => { + const {resource, memoryAdapter} = prepareResources(); + + const themeBuilder = new ThemeBuilder({fs: fsInterface(memoryAdapter)}); + + const [cssResource, cssRtlResource, jsonResource] = await themeBuilder.build([resource]); + + const expected = getExpectedResults({compress: false}); + t.is(await cssResource.getString(), expected.css, "CSS should be correct"); + t.is(await cssRtlResource.getString(), expected.cssRtl, "Right-to-left CSS should be correct"); + t.is(await jsonResource.getString(), expected.json, "JSON should be correct"); +}); + +test("ThemeBuilder: Builds a less file (compress = true)", async (t) => { + const {resource, memoryAdapter} = prepareResources(); + + const themeBuilder = new ThemeBuilder({fs: fsInterface(memoryAdapter)}); + + const [cssResource, cssRtlResource, jsonResource] = await themeBuilder.build([resource], { + compress: true + }); + + const expected = getExpectedResults({compress: true}); + t.is(await cssResource.getString(), expected.css, "CSS should be correct"); + t.is(await cssRtlResource.getString(), expected.cssRtl, "Right-to-left CSS should be correct"); + t.is(await jsonResource.getString(), expected.json, "JSON should be correct"); +}); + +test("ThemeBuilder: Builds a less file (compress = false)", async (t) => { + const {resource, memoryAdapter} = prepareResources(); + + const themeBuilder = new ThemeBuilder({fs: fsInterface(memoryAdapter)}); + + const [cssResource, cssRtlResource, jsonResource] = await themeBuilder.build([resource], { + compress: false + }); + + const expected = getExpectedResults({compress: false}); + t.is(await cssResource.getString(), expected.css, "CSS should be correct"); + t.is(await cssRtlResource.getString(), expected.cssRtl, "Right-to-left CSS should be correct"); + t.is(await jsonResource.getString(), expected.json, "JSON should be correct"); +}); + +test("ThemeBuilder: Builds a less file (no library)", async (t) => { + const {resource, memoryAdapter} = prepareResources({library: false}); + + const themeBuilder = new ThemeBuilder({fs: fsInterface(memoryAdapter)}); + + const [cssResource, cssRtlResource, jsonResource] = await themeBuilder.build([resource], { + compress: false + }); + + const expected = getExpectedResults({compress: false, library: false}); + t.is(await cssResource.getString(), expected.css, "CSS should be correct"); + t.is(await cssRtlResource.getString(), expected.cssRtl, "Right-to-left CSS should be correct"); + t.is(await jsonResource.getString(), expected.json, "JSON should be correct"); +});