import { existsSync , mkdirSync , writeFile } from 'fs' ; import { basename , dirname , resolve } from 'path' ; import { createFilter , CreateFilter } from 'rollup-pluginutils' ; import type { Plugin } from 'rollup' ; export interface CSSPluginOptions { exclude ? : Parameters < CreateFilter > [ 1 ] ; failOnError ? : boolean ; include ? : Parameters < CreateFilter > [ 0 ] ; includePaths ? : string [ ] ; insert ? : boolean ; output ? : string | false | ( ( css : string , styles : Styles ) => void ) ; prefix ? : string ; processor ? : ( css : string , map : string , styles : Styles ) => CSS | Promise < CSS > | PostCSSProcessor ; sass ? : SassRenderer ; sourceMap ? : boolean ; verbose ? : boolean ; watch ? : string | string [ ] ; } type ImporterReturnType = { file : string ; } | { contents : string ; } | Error | null ; type ImporterDoneCallback = ( data : ImporterReturnType ) => void ; type CSS = string | { css : string ; map : string ; } ; interface MappedCSS { css : string ; map : string ; } interface Styles { [ id ] : string ; } interface PostCSSProcessor { process : ( css : string , options ? : any ) => MappedCSS ; } interface SassRenderer { renderSync : ( options : SassOptions ) => SassResult ; } interface SassOptions { data : string ; } interface SassResult { css : Buffer ; map ? : Buffer ; } export default function scss ( options = { } ) { const filter = createFilter ( options . include || [ '/**/*.css' , '/**/*.scss' , '/**/*.sass' ] , options . exclude ) ; let dest = typeof options . output === 'string' ? options . output : null ; const insertStyleFnName = '___$insertStylesToHeader' ; const styles = { } ; const prefix = options . prefix ? options . prefix + '\n' : '' ; let includePaths = options . includePaths || [ 'node_modules/' ] ; includePaths . push ( process . cwd ( ) ) ; const compileToCSS = async function ( scss ) { if ( scss . length ) { includePaths = includePaths . filter ( ( v , i , a ) => a . indexOf ( v ) === i ) ; try { const sass = options . sass || loadSassLibrary ( ) ; const render = sass . renderSync ( Object . assign ( { data : prefix + scss , outFile : dest , includePaths , importer : ( url , prev , done ) : ImporterReturnType | void => { if ( url . startsWith ( '.' ) || url . startsWith ( 'url' ) || url . startsWith ( 'http' ) ) { return null ; } const cleanUrl = url . startsWith ( '~' ) ? url . replace ( '~' , '' ) : url ; try { const resolved = require . resolve ( cleanUrl , { paths : [ prefix + scss ] } ) ; return { file : resolved } ; } catch ( e ) { return null ; } } } , options ) ) ; const css = render . css . toString ( ) ; const map = render . map ? render . map . toString ( ) : '' ; if ( typeof options . processor === 'function' ) { const result = await options . processor ( css , map , styles ) ; const postcss = result ; if ( typeof postcss . process === 'function' ) { return Promise . resolve ( postcss . process ( css , { from : undefined , to : dest , map : map ? { prev : map , inline : false } : null } ) ) ; } const output = result ; return stringToCSS ( output ) ; } return { css , map } ; } catch ( e ) { if ( options . failOnError ) { throw e ; } console . log ( ) ; console . log ( red ( 'Error:\n\t' + e . message ) ) ; if ( e . message . includes ( 'Invalid CSS' ) ) { console . log ( green ( 'Solution:\n\t' + 'fix your Sass code' ) ) ; console . log ( 'Line:   ' + e . line ) ; console . log ( 'Column: ' + e . column ) ; } if ( e . message . includes ( 'sass' ) && e . message . includes ( 'find module' ) ) { console . log ( green ( 'Solution:\n\t' + 'npm install --save-dev sass' ) ) ; } if ( e . message . includes ( 'node-sass' ) && e . message . includes ( 'bindings' ) ) { console . log ( green ( 'Solution:\n\t' + 'npm rebuild node-sass --force' ) ) ; } console . log ( ) ; } } return { css : '' , map : '' } ; } ; return { name : 'scss' , intro ( ) { return options . insert === true ? insertStyleFn . replace ( /insertStyleFn/ , insertStyleFnName ) : '' ; } , async transform ( code , id ) { if ( ! filter ( id ) ) { return ; } includePaths . push ( dirname ( id ) ) ; if ( options . watch ) { const files = Array . isArray ( options . watch ) ? options . watch : [ options . watch ] ; files . forEach ( file => this . addWatchFile ( file ) ) ; } if ( options . insert === true ) { const { css , map } = await compileToCSS ( code ) ; return { code : 'export default ' + insertStyleFnName + '(' + JSON . stringify ( css ) + ')' , map : { mappings : '' } } ; } else if ( options . output === false ) { const { css , map } = await compileToCSS ( code ) ; return { code : 'export default ' + JSON . stringify ( css ) , map : { mappings : '' } } ; } styles [ id ] = code ; return '' ; } , async generateBundle ( opts ) { if ( options . output === false || options . insert === true ) { return ; } let scss = '' ; for ( const id in styles ) { scss += styles [ id ] || '' ; } if ( typeof dest !== 'string' ) { dest = opts . file || 'bundle.js' ; if ( dest . endsWith ( '.js' ) ) { dest = dest . slice ( 0 , - 3 ) ; } dest = dest + '.css' ; } const compiled = await compileToCSS ( scss ) ; if ( typeof compiled !== 'object' || typeof compiled . css !== 'string' ) { return ; } if ( typeof options . output === 'function' ) { options . output ( compiled . css , styles ) ; return ; } if ( ! compiled . css . length ) { return ; } ensureParentDirsSync ( dirname ( dest ) ) ; writeFile ( dest , compiled . css , err => { if ( options . verbose !== false ) { if ( err ) { console . error ( red ( err . toString ( ) ) ) ; } else if ( compiled . css ) { console . log ( green ( dest || '?' ) , getSize ( compiled . css . length ) ) ; } } } ) ; if ( options . sourceMap && compiled . map ) { let sourcemap = compiled . map ; if ( typeof compiled . map . toString === 'function' ) { sourcemap = compiled . map . toString ( ) ; } writeFile ( dest + '.map' , sourcemap , err => { if ( options . verbose !== false && err ) { console . error ( red ( err . toString ( ) ) ) ; } } ) ; } } } ; } param { String } css return { String } const insertStyleFn = `function insertStyleFn(css) {
  if (!css) {
    return
  }
  if (typeof window === 'undefined') {
    return
  }

  const style = document.createElement('style');

  style.setAttribute('type', 'text/css');
  style.innerHTML = css;
  document.head.appendChild(style);
  return css
}` ; function loadSassLibrary ( ) { try { return require ( 'sass' ) ; } catch ( e ) { return require ( 'node-sass' ) ; } } function stringToCSS ( input ) { if ( typeof input === 'string' ) { return { css : input , map : '' } ; } return input ; } function red ( text ) { return '\x1b[1m\x1b[31m' + text + '\x1b[0m' ; } function green ( text ) { return '\x1b[1m\x1b[32m' + text + '\x1b[0m' ; } function getSize ( bytes ) { return bytes < 10000 ? bytes . toFixed ( 0 ) + ' B' : bytes < 1024000 ? ( bytes / 1024 ) . toPrecision ( 3 ) + ' kB' : ( bytes / 1024 / 1024 ) . toPrecision ( 4 ) + ' MB' ; } function ensureParentDirsSync ( dir ) { if ( existsSync ( dir ) ) { return ; } try { mkdirSync ( dir ) ; } catch ( err ) { if ( err . code === 'ENOENT' ) { ensureParentDirsSync ( dirname ( dir ) ) ; ensureParentDirsSync ( dir ) ; } } }