@@ -15,6 +15,8 @@ const ImportCollectorPlugin = require("./plugin/import-collector");
15
15
const VariableCollectorPlugin = require ( "./plugin/variable-collector" ) ;
16
16
const UrlCollector = require ( "./plugin/url-collector" ) ;
17
17
18
+ const LESS_OPENUI5_VERSION = require ( "../package.json" ) . version ;
19
+
18
20
// Workaround for a performance issue in the "css" parser module when used in combination
19
21
// with the "colors" module that enhances the String prototype.
20
22
// See: https://github.com/reworkcss/css/issues/88
@@ -104,10 +106,14 @@ Builder.prototype.cacheTheme = function(result) {
104
106
} ;
105
107
106
108
/**
107
- * Creates a themebuild
109
+ * Runs a theme build
108
110
* @param {object } options
109
111
* @param {object } options.compiler compiler object as passed to less
110
112
* @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
111
117
* @param {string } options.lessInput less string input
112
118
* @param {string } options.lessInputPath less file input
113
119
* @returns {{css: string, cssRtl: string, variables: {}, imports: [], cssSkeleton: string, cssSkeletonRtl: string, cssVariables: string, cssVariablesSource: string } }
@@ -128,7 +134,9 @@ Builder.prototype.build = function(options) {
128
134
parser : { } ,
129
135
compiler : { } ,
130
136
library : { } ,
131
- scope : { }
137
+ scope : { } ,
138
+ inlineThemingParameters : true ,
139
+ inlineCssVariables : false ,
132
140
} , options ) ;
133
141
134
142
if ( options . compiler . sourceMap ) {
@@ -178,7 +186,7 @@ Builder.prototype.build = function(options) {
178
186
} ) ;
179
187
}
180
188
181
- function compile ( config ) {
189
+ async function compile ( config ) {
182
190
const parserOptions = clone ( options . parser ) ;
183
191
let rootpath ;
184
192
@@ -208,151 +216,193 @@ Builder.prototype.build = function(options) {
208
216
209
217
const parser = createParser ( parserOptions , fnFileHandler ) ;
210
218
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
+ } ) ;
218
228
} ) ;
219
- } ) . then ( async function ( tree ) {
220
- const result = { } ;
221
-
222
- result . tree = tree ;
229
+ }
223
230
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} ;
230
233
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 ) ;
235
274
236
- // retrieve imported files
237
- result . imports = oImportCollector . getImports ( ) ;
275
+ oRTL . setExistingImgRtlPaths ( existingImgRtlUrls ) ;
276
+ }
238
277
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
+ }
241
283
242
- // retrieve all variables
243
- result . allVariables = oVariableCollector . getAllVariables ( ) ;
284
+ if ( rootpath ) {
285
+ result . imports . unshift ( rootpath ) ;
286
+ }
244
287
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 ) ;
264
292
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 ) ;
267
302
303
+ let cssSkeletonRtl ;
268
304
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 ]
271
308
} ) ) ;
272
309
}
273
310
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 ;
277
319
278
- // also compile css-variables version if requested
320
+ // Only add additional CSS Variables content if requested
279
321
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 ;
322
327
}
328
+ }
323
329
324
- return result ;
325
- } ) ;
330
+ return result ;
326
331
}
327
332
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
+ }
332
338
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 ) ;
338
341
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
+ } ) ;
343
347
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
352
390
}
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 ;
353
402
}
354
- resolve ( result ) ;
355
- } ) ;
403
+ }
404
+
405
+ return result ;
356
406
}
357
407
358
408
function getScopeVariables ( options ) {
0 commit comments