@@ -15,6 +15,8 @@ const ImportCollectorPlugin = require("./plugin/import-collector");
1515const VariableCollectorPlugin = require ( "./plugin/variable-collector" ) ;
1616const UrlCollector = require ( "./plugin/url-collector" ) ;
1717
18+ const LESS_OPENUI5_VERSION = require ( "../package.json" ) . version ;
19+
1820// Workaround for a performance issue in the "css" parser module when used in combination
1921// with the "colors" module that enhances the String prototype.
2022// See: https://github.com/reworkcss/css/issues/88
@@ -104,10 +106,14 @@ Builder.prototype.cacheTheme = function(result) {
104106} ;
105107
106108/**
107- * Creates a themebuild
109+ * Runs a theme build
108110 * @param {object } options
109111 * @param {object } options.compiler compiler object as passed to less
110112 * @param {boolean } options.cssVariables whether or not to enable css variables output
113+ * @param {boolean } [options.inlineThemingParameters=true] Whether theming parameters should be inlined into the
114+ * library CSS content via background-image
115+ * @param {boolean } [options.inlineCssVariables=false] Whether theming parameters should be inlined into the
116+ * library CSS content via CSS Variables
111117 * @param {string } options.lessInput less string input
112118 * @param {string } options.lessInputPath less file input
113119 * @returns {{css: string, cssRtl: string, variables: {}, imports: [], cssSkeleton: string, cssSkeletonRtl: string, cssVariables: string, cssVariablesSource: string } }
@@ -128,7 +134,9 @@ Builder.prototype.build = function(options) {
128134 parser : { } ,
129135 compiler : { } ,
130136 library : { } ,
131- scope : { }
137+ scope : { } ,
138+ inlineThemingParameters : true ,
139+ inlineCssVariables : false ,
132140 } , options ) ;
133141
134142 if ( options . compiler . sourceMap ) {
@@ -178,7 +186,7 @@ Builder.prototype.build = function(options) {
178186 } ) ;
179187 }
180188
181- function compile ( config ) {
189+ async function compile ( config ) {
182190 const parserOptions = clone ( options . parser ) ;
183191 let rootpath ;
184192
@@ -208,151 +216,193 @@ Builder.prototype.build = function(options) {
208216
209217 const parser = createParser ( parserOptions , fnFileHandler ) ;
210218
211- return new Promise ( function ( resolve , reject ) {
212- parser . parse ( config . content , function ( err , tree ) {
213- if ( err ) {
214- reject ( err ) ;
215- } else {
216- resolve ( tree ) ;
217- }
219+ function parseContent ( content ) {
220+ return new Promise ( function ( resolve , reject ) {
221+ parser . parse ( content , function ( err , tree ) {
222+ if ( err ) {
223+ reject ( err ) ;
224+ } else {
225+ resolve ( tree ) ;
226+ }
227+ } ) ;
218228 } ) ;
219- } ) . then ( async function ( tree ) {
220- const result = { } ;
221-
222- result . tree = tree ;
229+ }
223230
224- // plugins to collect imported files and variable values
225- const oImportCollector = new ImportCollectorPlugin ( {
226- importMappings : mFileMappings [ filename ]
227- } ) ;
228- const oVariableCollector = new VariableCollectorPlugin ( options . compiler ) ;
229- const oUrlCollector = new UrlCollector ( ) ;
231+ const tree = await parseContent ( config . content ) ;
232+ const result = { tree} ;
230233
231- // render to css
232- result . css = tree . toCSS ( Object . assign ( { } , options . compiler , {
233- plugins : [ oImportCollector , oVariableCollector , oUrlCollector ]
234- } ) ) ;
234+ // plugins to collect imported files and variable values
235+ const oImportCollector = new ImportCollectorPlugin ( {
236+ importMappings : mFileMappings [ filename ]
237+ } ) ;
238+ const oVariableCollector = new VariableCollectorPlugin ( options . compiler ) ;
239+ const oUrlCollector = new UrlCollector ( ) ;
240+
241+ // render to css
242+ result . css = tree . toCSS ( Object . assign ( { } , options . compiler , {
243+ plugins : [ oImportCollector , oVariableCollector , oUrlCollector ]
244+ } ) ) ;
245+
246+ // retrieve imported files
247+ result . imports = oImportCollector . getImports ( ) ;
248+
249+ // retrieve reduced set of variables
250+ result . variables = oVariableCollector . getVariables ( Object . keys ( mFileMappings [ filename ] || { } ) ) ;
251+
252+ // retrieve all variables
253+ result . allVariables = oVariableCollector . getAllVariables ( ) ;
254+
255+ // also compile rtl-version if requested
256+ let oRTL ;
257+ if ( options . rtl ) {
258+ const RTLPlugin = require ( "./plugin/rtl" ) ;
259+ oRTL = new RTLPlugin ( ) ;
260+
261+ const urls = oUrlCollector . getUrls ( ) ;
262+
263+ const existingImgRtlUrls = ( await Promise . all (
264+ urls . map ( async ( { currentDirectory, relativeUrl} ) => {
265+ const relativeImgRtlUrl = RTLPlugin . getRtlImgUrl ( relativeUrl ) ;
266+ if ( relativeImgRtlUrl ) {
267+ const resolvedImgRtlUrl = path . posix . join ( currentDirectory , relativeImgRtlUrl ) ;
268+ if ( await that . fileUtils . findFile ( resolvedImgRtlUrl , options . rootPaths ) ) {
269+ return resolvedImgRtlUrl ;
270+ }
271+ }
272+ } )
273+ ) ) . filter ( Boolean ) ;
235274
236- // retrieve imported files
237- result . imports = oImportCollector . getImports ( ) ;
275+ oRTL . setExistingImgRtlPaths ( existingImgRtlUrls ) ;
276+ }
238277
239- // retrieve reduced set of variables
240- result . variables = oVariableCollector . getVariables ( Object . keys ( mFileMappings [ filename ] || { } ) ) ;
278+ if ( oRTL ) {
279+ result . cssRtl = tree . toCSS ( Object . assign ( { } , options . compiler , {
280+ plugins : [ oRTL ]
281+ } ) ) ;
282+ }
241283
242- // retrieve all variables
243- result . allVariables = oVariableCollector . getAllVariables ( ) ;
284+ if ( rootpath ) {
285+ result . imports . unshift ( rootpath ) ;
286+ }
244287
245- // also compile rtl-version if requested
246- let oRTL ;
247- if ( options . rtl ) {
248- const RTLPlugin = require ( "./plugin/rtl" ) ;
249- oRTL = new RTLPlugin ( ) ;
250-
251- const urls = oUrlCollector . getUrls ( ) ;
252-
253- const existingImgRtlUrls = ( await Promise . all (
254- urls . map ( async ( { currentDirectory, relativeUrl} ) => {
255- const relativeImgRtlUrl = RTLPlugin . getRtlImgUrl ( relativeUrl ) ;
256- if ( relativeImgRtlUrl ) {
257- const resolvedImgRtlUrl = path . posix . join ( currentDirectory , relativeImgRtlUrl ) ;
258- if ( await that . fileUtils . findFile ( resolvedImgRtlUrl , options . rootPaths ) ) {
259- return resolvedImgRtlUrl ;
260- }
261- }
262- } )
263- ) ) . filter ( Boolean ) ;
288+ // also compile css-variables version if requested
289+ if ( options . cssVariables || options . inlineCssVariables ) {
290+ // parse the content again to have a clean tree
291+ const cssVariablesSkeletonTree = await parseContent ( config . content ) ;
264292
265- oRTL . setExistingImgRtlPaths ( existingImgRtlUrls ) ;
266- }
293+ // generate the skeleton-css and the less-variables
294+ const CSSVariablesCollectorPlugin = require ( "./plugin/css-variables-collector" ) ;
295+ const oCSSVariablesCollector = new CSSVariablesCollectorPlugin ( config ) ;
296+ const oVariableCollector = new VariableCollectorPlugin ( options . compiler ) ;
297+ const cssSkeleton = cssVariablesSkeletonTree . toCSS ( Object . assign ( { } , options . compiler , {
298+ plugins : [ oCSSVariablesCollector , oVariableCollector ]
299+ } ) ) ;
300+ const varsOverride = oVariableCollector . getAllVariables ( ) ;
301+ const cssVariablesSource = oCSSVariablesCollector . toLessVariables ( varsOverride ) ;
267302
303+ let cssSkeletonRtl ;
268304 if ( oRTL ) {
269- result . cssRtl = tree . toCSS ( Object . assign ( { } , options . compiler , {
270- plugins : [ oRTL ]
305+ const oCSSVariablesCollectorRTL = new CSSVariablesCollectorPlugin ( config ) ;
306+ cssSkeletonRtl = cssVariablesSkeletonTree . toCSS ( Object . assign ( { } , options . compiler , {
307+ plugins : [ oCSSVariablesCollectorRTL , oRTL ]
271308 } ) ) ;
272309 }
273310
274- if ( rootpath ) {
275- result . imports . unshift ( rootpath ) ;
276- }
311+ // generate the css-variables content out of the less-variables
312+ const cssVariablesTree = await parseContent ( cssVariablesSource ) ;
313+ const CSSVariablesPointerPlugin = require ( "./plugin/css-variables-pointer" ) ;
314+ const cssVariables = cssVariablesTree . toCSS ( Object . assign ( { } , options . compiler , {
315+ plugins : [ new CSSVariablesPointerPlugin ( ) ]
316+ } ) ) ;
317+
318+ result . cssVariables = cssVariables ;
277319
278- // also compile css-variables version if requested
320+ // Only add additional CSS Variables content if requested
279321 if ( options . cssVariables ) {
280- return new Promise ( function ( resolve , reject ) {
281- // parse the content again to have a clean tree
282- parser . parse ( config . content , function ( err , tree ) {
283- if ( err ) {
284- reject ( err ) ;
285- } else {
286- resolve ( tree ) ;
287- }
288- } ) ;
289- } ) . then ( function ( tree ) {
290- // generate the skeleton-css and the less-variables
291- const CSSVariablesCollectorPlugin = require ( "./plugin/css-variables-collector" ) ;
292- const oCSSVariablesCollector = new CSSVariablesCollectorPlugin ( config ) ;
293- const oVariableCollector = new VariableCollectorPlugin ( options . compiler ) ;
294- result . cssSkeleton = tree . toCSS ( Object . assign ( { } , options . compiler , {
295- plugins : [ oCSSVariablesCollector , oVariableCollector ]
296- } ) ) ;
297- const varsOverride = oVariableCollector . getAllVariables ( ) ;
298- result . cssVariablesSource = oCSSVariablesCollector . toLessVariables ( varsOverride ) ;
299- if ( oRTL ) {
300- const oCSSVariablesCollectorRTL = new CSSVariablesCollectorPlugin ( config ) ;
301- result . cssSkeletonRtl = tree . toCSS ( Object . assign ( { } , options . compiler , {
302- plugins : [ oCSSVariablesCollectorRTL , oRTL ]
303- } ) ) ;
304- }
305- return tree ;
306- } ) . then ( function ( tree ) {
307- // generate the css-variables content out of the less-variables
308- return new Promise ( function ( resolve , reject ) {
309- parser . parse ( result . cssVariablesSource , function ( err , tree ) {
310- if ( err ) {
311- reject ( err ) ;
312- } else {
313- const CSSVariablesPointerPlugin = require ( "./plugin/css-variables-pointer" ) ;
314- result . cssVariables = tree . toCSS ( Object . assign ( { } , options . compiler , {
315- plugins : [ new CSSVariablesPointerPlugin ( ) ]
316- } ) ) ;
317- resolve ( result ) ;
318- }
319- } ) ;
320- } ) ;
321- } ) ;
322+ result . cssSkeleton = cssSkeleton ;
323+ if ( oRTL ) {
324+ result . cssSkeletonRtl = cssSkeletonRtl ;
325+ }
326+ result . cssVariablesSource = cssVariablesSource ;
322327 }
328+ }
323329
324- return result ;
325- } ) ;
330+ return result ;
326331 }
327332
328- function addInlineParameters ( result ) {
329- return new Promise ( function ( resolve , reject ) {
330- if ( typeof options . library === "object" && typeof options . library . name === "string" ) {
331- const parameters = JSON . stringify ( result . variables ) ;
333+ async function addInlineParameters ( result ) {
334+ // Inline parameters can only be added when the library name is known
335+ if ( typeof options . library !== "object" || typeof options . library . name !== "string" ) {
336+ return result ;
337+ }
332338
333- // properly escape the parameters to be part of a data-uri
334- // + escaping single quote (') as it is used to surround the data-uri: url('...')
335- const escapedParameters = encodeURIComponent ( parameters ) . replace ( / ' / g, function ( char ) {
336- return escape ( char ) ;
337- } ) ;
339+ if ( options . inlineThemingParameters === true ) {
340+ const parameters = JSON . stringify ( result . variables ) ;
338341
339- // embed parameter variables as plain-text string into css
340- const parameterStyleRule = "\n/* Inline theming parameters */\n#sap-ui-theme-" +
341- options . library . name . replace ( / \. / g, "\\." ) +
342- "{background-image:url('data:text/plain;utf-8," + escapedParameters + "')}\n" ;
342+ // properly escape the parameters to be part of a data-uri
343+ // + escaping single quote (') as it is used to surround the data-uri: url('...')
344+ const escapedParameters = encodeURIComponent ( parameters ) . replace ( / ' / g, function ( char ) {
345+ return escape ( char ) ;
346+ } ) ;
343347
344- // embed parameter variables as plain-text string into css
345- result . css += parameterStyleRule ;
346- if ( options . rtl ) {
347- result . cssRtl += parameterStyleRule ;
348- }
349- if ( options . cssVariables ) {
350- // for the css variables build we just add it to the variables
351- result . cssVariables += parameterStyleRule ;
348+ // embed parameter variables as plain-text string into css
349+ const parameterStyleRule = "\n/* Inline theming parameters */\n#sap-ui-theme-" +
350+ options . library . name . replace ( / \. / g, "\\." ) +
351+ "{background-image:url('data:text/plain;utf-8," + escapedParameters + "')}\n" ;
352+
353+ // embed parameter variables as plain-text string into css
354+ result . css += parameterStyleRule ;
355+ if ( options . rtl ) {
356+ result . cssRtl += parameterStyleRule ;
357+ }
358+ if ( options . cssVariables ) {
359+ // for the css variables build we just add it to the variables
360+ result . cssVariables += parameterStyleRule ;
361+ }
362+ }
363+
364+ if ( options . inlineCssVariables === true ) {
365+ let scopes ;
366+ if ( typeof result . variables . scopes === "object" ) {
367+ scopes = Object . keys ( result . variables . scopes ) ;
368+ } else {
369+ scopes = [ ] ;
370+ }
371+
372+ const libraryNameSlashed = options . library . name . replace ( / \. / g, "/" ) ;
373+ const libraryNameDashed = options . library . name . replace ( / \. / g, "-" ) ;
374+
375+ // TODO: How to get theme name? .theming "sId"? parse from file path? new parameter?
376+ const themeId = "<theme-name>" ;
377+
378+ const metadataJson = JSON . stringify ( {
379+ "Path" : `UI5.${ libraryNameSlashed } .${ themeId } .library` ,
380+ "PathPattern" : "/%frameworkId%/%libId%/themes/%themeId%/%fileId%.css" ,
381+ "Extends" : [ "base" ] , // TODO: Read from .theming?
382+ "Scopes" : scopes ,
383+ "Engine" : {
384+ "Version" : LESS_OPENUI5_VERSION ,
385+ "Name" : "less-openui5"
386+ } ,
387+ "Version" : {
388+ "Build" : "<TODO>" , // TOOD: add new property options.library.version
389+ "Source" : "<TODO>" // TOOD: add new property options.library.version
352390 }
391+ } ) ;
392+
393+ const additionalVariables = `
394+ :root {
395+ --sapUiTheme-${ libraryNameDashed } : true;
396+ --sapThemeMetaData-UI5-${ libraryNameDashed } : ${ metadataJson } ;
397+ }
398+ ` ;
399+ result . css = additionalVariables + result . cssVariables + "\n" + result . css ;
400+ if ( options . rtl ) {
401+ result . cssRtl = additionalVariables + result . cssVariables + "\n" + result . cssRtl ;
353402 }
354- resolve ( result ) ;
355- } ) ;
403+ }
404+
405+ return result ;
356406 }
357407
358408 function getScopeVariables ( options ) {
0 commit comments