/*
expansive.es - Configuration for exp-css
Transform by prefixing and minifying. Uses postcss and less.
*/
Expansive.load({
services: {
name: 'css',
dotmin: true,
files: [ '**.css*', '!**.map', '!*.less*' ],
force: false,
extract: false,
minify: false,
prefix: true,
usemap: true,
usemin: true,
transforms: [{
mappings: {
'css',
'min.css'
'css.map'
},
init: function(transform) {
let service = transform.service
if (service.minify) {
service.usemin ||= true
}
if (service.usemap) {
service.usemin ||= true
}
if (service.files) {
if (!(service.files is Array)) {
service.files = [ service.files ]
}
if (expansive.control.collections.styles.length == 0) {
expansive.control.collections.styles = service.files
}
}
if (!service.minify) {
expansive.transforms['css-minify'].enable = false
}
if (!service.extract) {
expansive.transforms['css-extract'].enable = false
}
}
resolve: function(path: Path, transform): Path? {
let service = transform.service
let vfile = directories.contents.join(path)
if (vfile.endsWith('.min.css')) {
let base = vfile.trimEnd('.min.css')
if (service.usemin && !(service.minify && service.force && base.joinExt('css', true).exists)) {
if (service.usemap) {
if (base.joinExt('css.map', true).exists) {
return terminal(path)
}
} else {
return terminal(path)
}
}
} else if (vfile.endsWith('.css.map')) {
if (service.usemin) {
let base = vfile.trimEnd('.css.map')
if (service.usemap && !(service.minify && service.force && base.joinExt('css', true).exists)) {
if (base.joinExt('min.css', true).exists) {
return terminal(path)
}
}
}
} else {
let minified = vfile.replaceExt('min.css')
/*
Use this source file if forced+miify, or a suitable minfied version does not exist or !usemin
*/
if ((service.minify && service.force) ||
!(minified.exists && service.usemin && (!service.usemap || vfile.replaceExt('css.map').exists))) {
if (service.minify && service.dotmin) {
return terminal(path.trimExt().joinExt('min.css', true))
}
return terminal(path)
}
}
return null
}
}, {
name: 'prefix',
mappings: 'css',
render: function(contents) {
let postcss = Cmd.locate('postcss')
if (postcss) {
contents = expansive.run(postcss + ' --use autoprefixer', contents)
} else {
trace('Warn', 'Cannot find postcss')
}
return contents
}
}, {
name: 'minify',
mappings: 'css',
render: function(contents, meta) {
trace('Minify', meta.current)
let less = Cmd.locate('lessc')
if (less) {
contents = expansive.run(less + ' --compress - ', contents, meta)
} else {
trace('Warn', 'Cannot find lessc')
}
return contents
}
}, {
name: 'render',
mappings: {
'css',
'min.css'
},
init: function(transform) {
let service = transform.service
service.hash = {}
/*
Render styles is based on 'collections.styles' which defaults to '**.css' and is modified via expansive.json and addItems.
*/
global.renderStyles = function(filter = null, extras = []) {
let collections = expansive.collections
if (!collections.styles) {
return
}
function buildStyleList(files: Array): Array {
let directories = expansive.directories
let service = expansive.services.css
let styles = []
for each (style in files) {
if ((style = expansive.getDestPath(style)) == null) {
continue
}
let vfile = directories.contents.join(style)
let base = vfile.trimEnd('.min.css').trimEnd('.css')
let map = base.joinExt('min.map', true).exists || base.joinExt('css.map', true).exists ||
base.joinExt('.min.css.map', true).exists
if (vfile.endsWith('min.css')) {
if (service.usemin && (!service.usemap || map)) {
styles.push(style)
}
} else {
let minified = vfile.replaceExt('min.css').exists
let map = vfile.replaceExt('min.map').exists || vfile.replaceExt('css.map').exists ||
vfile.replaceExt('.min.css.map').exists
if ((service.minify && service.force) || !minified || !(service.usemap && map)) {
if (service.minify && service.dotmin) {
styles.push(style.replaceExt('min.css'))
} else {
styles.push(style)
}
}
}
}
return styles
}
let directories = expansive.directories
let service = expansive.services.css
/*
Pages have different stylesheets and so must compute style list per page.
This is hased and saved.
*/
if (!service.hash[collections.styles]) {
let files = directories.contents.files(collections.styles,
{ contents: true, directories: false, relative: true})
if (expansive.control.filters) {
files = files.filter(function(e) { return e.glob(expansive.control.filters) })
}
files = expansive.orderFiles(files, "css")
service.hash[collections.styles] = buildStyleList(files).unique()
}
for each (style in service.hash[collections.styles]) {
if (filter && !Path(style).glob(filter)) {
continue
}
if (service.absolute) {
if (!style.startsWith('http') && !style.startsWith('..')) {
style = '/' + style
}
} else {
style = meta.top.join(style).trimStart('./')
}
write('\n ')
}
if (extras && extras is String) {
extras = [extras]
}
if (service.states) {
let extracted = service.states[meta.destPath]
if (extracted && extracted.link) {
let ct = expansive.transforms.css
extras.push(ct.resolve(extracted.link, ct))
}
}
for each (style in extras) {
if (service.absolute) {
if (!style.startsWith('http') && !style.startsWith('..')) {
style = '/' + style
}
} else {
style = meta.top.join(style).trimStart('./')
}
write('\n ')
}
if (expansive.collections['inline-styles']) {
write('')
}
}
},
pre: function (transform) {
if (expansive.modified.everything) {
transform.service.hash = {}
}
},
}, {
name: 'extract',
mappings: 'html',
init: function(transform) {
let service = transform
if (!service.extract) {
transform.enable = false
return
}
transform.nextId = 0
service.states = {}
},
render: function(contents, meta, transform) {
/*
Local function to handle inline style elements
*/
function handleStyleElements(contents, meta, state, transform): String {
let re = /