import fs from 'fs' ; import { TransformCallback } from 'stream' ; import replaceExtension from 'replace-ext' ; import applySourceMap from 'vinyl-sourcemaps-apply' ; import { LooseObject , BalmError } from '@balm-core/index' ; const PLUGIN_NAME = 'less' ; interface LessResultSourcemap { sourcesContent : any ; file : string ; sources : string [ ] ; } interface LessResult { result : string ; imports : string [ ] ; sourcemap ? : LessResultSourcemap ; css ? : string ; } function inlineSources ( sourcemap ) { if ( sourcemap . sourcesContent ) { return Promise . resolve ( sourcemap ) ; } return Promise . all ( sourcemap . sources . map ( ( source ) => { return new Promise ( ( resolve , reject ) => { fs . readFile ( source , 'utf8' , ( err , data ) => { if ( err ) { reject ( err ) ; } else { resolve ( data ) ; } } ) ; } ) ; } ) ) . then ( ( contents ) => { sourcemap . sourcesContent = contents ; return sourcemap ; } , ( ) => sourcemap ) ; } function renderLess ( str , opts ) { return new Promise ( ( resolve , reject ) => { require ( 'less' ) . render ( str , opts , ( err , res ) => { if ( err ) { reject ( err ) ; } else { const obj = { result : res . css , imports : res . imports } ; if ( opts . sourceMap && res . map ) { obj . sourcemap = JSON . parse ( res . map ) as LessResultSourcemap ; inlineSources ( obj . sourcemap ) . then ( ( map ) => { obj . sourcemap = map ; resolve ( obj ) ; } ) ; } else { resolve ( obj ) ; } } } ) ; } ) ; } function gulpLess ( options ) { const opts = Object . assign ( { } , { compress : false , paths : [ ] } , options ) ; return through2 . obj ( ( file , enc , cb ) => { if ( file . isNull ( ) ) { return cb ( null , file ) ; } if ( file . isStream ( ) ) { return cb ( new PluginError ( PLUGIN_NAME , 'Streaming not supported' ) ) ; } const str = file . contents . toString ( ) as string ; opts . filename = file . path ; if ( file . sourceMap || opts . sourcemap ) { opts . sourceMap = true ; } renderLess ( str , opts ) . then ( ( res ) => { file . contents = Buffer . from ( res . result as string ) ; file . path = replaceExtension ( file . path , '.css' ) ; if ( res . sourcemap ) { res . sourcemap . file = file . relative ; res . sourcemap . sources = res . sourcemap . sources . map ( ( source ) => path . relative ( file . base as string , source ) ) ; applySourceMap ( file , res . sourcemap ) ; } return file ; } ) . then ( ( file ) => { cb ( null , file ) ; } ) . catch ( ( err ) => { err . lineNumber = err . line ; err . fileName = err . filename ; err . message = `${ err . message } in file ${ err . fileName } line no.${ err . lineNumber }` ; return cb ( new PluginError ( PLUGIN_NAME , err as BalmError ) ) ; } ) ; } ) ; } export default gulpLess ;