import * as fs from "fs" ; import less from "less" ; import mkpath from "mkpath" ; import * as path from "path" ; import * as vscode from "vscode" ; import { Uri } from "vscode" ; import * as Configuration from "./Configuration" ; import { EasyLessOptions } from "./Configuration" ; import * as FileOptionsParser from "./FileOptionsParser" ; import { LessDocumentResolverPlugin } from "./LessDocumentResolverPlugin" ; const DEFAULT_EXT = ".css" ; export async function compile ( lessFile , content , defaults ) { const options = FileOptionsParser . parse ( content , defaults ) ; const lessPath = path . dirname ( lessFile ) ; if ( options . main ) { const mainFilePaths = resolveMainFilePaths ( options . main , lessPath , lessFile ) ; if ( mainFilePaths && mainFilePaths . length > 0 ) { for ( const filePath of mainFilePaths ) { const mainPath = path . parse ( filePath ) ; const mainRootFileInfo = Configuration . getRootFileInfo ( mainPath ) ; const mainDefaults = { ... defaults , rootFileInfo : mainRootFileInfo } ; const mainContent = await readFilePromise ( filePath , "utf-8" ) ; await compile ( filePath , mainContent , mainDefaults ) ; } return ; } } if ( options . out === null || options . out === false ) { return ; } const out = options . out ; const extension = chooseExtension ( options ) ; const baseFilename = path . parse ( lessFile ) . name ; let cssRelativeFilename ; if ( typeof out === "string" ) { const interpolatedOut = intepolatePath ( out . replace ( '$1' , baseFilename ) . replace ( '$2' , extension ) , lessFile ) ; cssRelativeFilename = interpolatedOut ; const lastCharacter = cssRelativeFilename . slice ( - 1 ) ; if ( lastCharacter === "/" || lastCharacter === "\\" ) { cssRelativeFilename += baseFilename + extension ; } else if ( path . extname ( cssRelativeFilename ) === "" ) { cssRelativeFilename += extension ; } } else { cssRelativeFilename = baseFilename + extension ; } const cssFile = path . resolve ( lessPath , cssRelativeFilename ) ; delete options . out ; let sourceMapFile ; if ( options . sourceMap ) { const lessPath = path . parse ( lessFile ) . dir ; const cssPath = path . parse ( cssFile ) . dir ; const lessRelativeToCss = path . relative ( cssPath , lessPath ) ; const sourceMapOptions = < Less . SourceMapOption > { outputSourceFiles : false , sourceMapBasepath : lessPath , sourceMapFileInline : options . sourceMapFileInline , sourceMapRootpath : lessRelativeToCss , } ; if ( ! sourceMapOptions . sourceMapFileInline ) { sourceMapFile = cssFile + ".map" ; const sourceMapFilename = path . parse ( sourceMapFile ) . base ; sourceMapOptions . sourceMapURL = "./" + sourceMapFilename ; } options . sourceMap = sourceMapOptions ; } options . plugins = [ ] ; if ( options . autoprefixer ) { const LessPluginAutoPrefix = require ( "less-plugin-autoprefix" ) ; const browsers = cleanBrowsersList ( options . autoprefixer ) ; const autoprefixPlugin = new LessPluginAutoPrefix ( { browsers } ) ; options . plugins . push ( autoprefixPlugin ) ; } options . plugins . push ( new LessDocumentResolverPlugin ( ) ) ; const output = await less . render ( content , options ) ; await writeFileContents ( cssFile , output . css ) ; if ( output . map && sourceMapFile ) { await writeFileContents ( sourceMapFile , output . map ) ; } } function cleanBrowsersList ( autoprefixOption ) { const browsers = Array . isArray ( autoprefixOption ) ? autoprefixOption : ( "" + autoprefixOption ) . split ( /,|;/ ) ; return browsers . map ( ( browser ) => browser . trim ( ) ) ; } function intepolatePath ( this , path , lessFilePath ) { if ( path . includes ( "${workspaceFolder}" ) ) { const lessFileUri = Uri . file ( lessFilePath ) ; const workspaceFolder = vscode . workspace . getWorkspaceFolder ( lessFileUri ) ; if ( workspaceFolder ) { path = path . replace ( /\$\{workspaceFolder\}/g , workspaceFolder . uri . fsPath ) ; } } if ( path . includes ( "${workspaceRoot}" ) ) { if ( vscode . workspace . rootPath ) { path = path . replace ( /\$\{workspaceRoot\}/g , vscode . workspace . rootPath ) ; } } return path ; } function resolveMainFilePaths ( this , main , lessPath , currentLessFile ) { let mainFiles ; if ( typeof main === "string" ) { mainFiles = [ main ] ; } else if ( Array . isArray ( main ) ) { mainFiles = main ; } else { mainFiles = [ ] ; } const interpolatedMainFilePaths = mainFiles . map ( ( mainFile ) => intepolatePath ( mainFile , lessPath ) ) ; const resolvedMainFilePaths = interpolatedMainFilePaths . map ( ( mainFile ) => path . resolve ( lessPath , mainFile ) ) ; if ( resolvedMainFilePaths . indexOf ( currentLessFile ) >= 0 ) { return [ ] ; } return resolvedMainFilePaths ; } function writeFileContents ( this , filepath , content ) { return new Promise ( ( resolve , reject ) => { mkpath ( path . dirname ( filepath ) , ( err ) => { if ( err ) { return reject ( err ) ; } fs . writeFile ( filepath , content . toString ( ) , ( err ) => { if ( err ) { reject ( err ) ; } else { resolve ( void 0 ) ; } } ) ; } ) ; } ) ; } function readFilePromise ( this , filename , encoding ) { return new Promise ( ( resolve , reject ) => { fs . readFile ( filename , encoding , ( err , data ) => { if ( err ) { reject ( err ) ; } else { resolve ( data ) ; } } ) ; } ) ; } function chooseExtension ( this , options ) { if ( options && options . outExt ) { if ( options . outExt === "" ) { return "" ; } return ensureDotPrefixed ( options . outExt ) || DEFAULT_EXT ; } return DEFAULT_EXT ; } function ensureDotPrefixed ( this , extension ) { if ( extension . startsWith ( "." ) ) { return extension ; } return extension ? `.${ extension }` : "" ; }