Git Product home page Git Product logo

Comments (2)

ryanoglesby08 avatar ryanoglesby08 commented on July 28, 2024 1

I had this same problem and solved it a slightly different way.

I injected a custom source file loader into the postcss-modules options in my rollup config, using the Loader option.

The custom Loader is based on the FileSystemLoader found in css-modules-loader-core: https://github.com/css-modules/css-modules-loader-core/blob/master/src/file-system-loader.js
But instead of using Node's fs to read files, it uses node-sass to read files and render them with Sass during the composition process.

I think this way is less hacky, and doesn't require any extra temporary files. I don't like that I have to copy the FileSystemLoader completely, but a comment at the top makes it more apparent why I've copied it.

Here is my rollup.config.js and my custom loader.

// Abbreviated rollup.config.js

import path from 'path'

import sass from 'node-sass'
import postcss from 'rollup-plugin-postcss'
import postcssModules from 'postcss-modules'
import autoprefixer from 'autoprefixer'
import CssModulesSassLoader from './CssModulesSassLoader'

const cssExportMap = {}

const sassPreprocessor = (content, id) => new Promise((resolve) => {
  const result = sass.renderSync({ file: id })
  resolve({ code: result.css.toString() })
})

export default {
  ... other rollup config ...

  plugins: [
    ... other plugins ...

    postcss({
      extract: path.resolve('./dist/components.css'),
      sourceMap: true,
      extensions: ['.scss'],
      preprocessor: sassPreprocessor, // Pre-process all imports with Sass
      plugins: [
        autoprefixer(),
        postcssModules({
          Loader: CssModulesSassLoader, // Load all "composes" files with Sass
          generateScopedName: '[name]__[local]___[hash:base64:5]',
          getJSON(id, exportTokens) {
            cssExportMap[id] = exportTokens
          }
        })
      ],
      getExportNamed: false,
      getExport(id) {
        return cssExportMap[id]
      }
    })
  ]
}
// CssModulesSassLoader.js

/**
 * This is a copy of the FileSystemLoader from https://github.com/css-modules/css-modules-loader-core/blob/master/src/file-system-loader.js
 * with one key difference. Instead of loading files using Node's built-int file system module (`fs`), it loads files
 * with Sass. This allows us to use Sass with CSS Modules `composes`:
 *
 * .someClass {
 *   composes: otherClass from '../Other/Other.scss'
 * }
 *
 * The original loading code with `fs` is commented out starting on line 70, and is replaced with `sass.render` on line 81.
 * **There are no other modifications.**
 *
 *
 */

import path from 'path'
import Core from 'css-modules-loader-core'
import sass from 'node-sass'

// Sorts dependencies in the following way:
// AAA comes before AA and A
// AB comes after AA and before A
// All Bs come after all As
// This ensures that the files are always returned in the following order:
// - In the order they were required, except
// - After all their dependencies
const traceKeySorter = (a, b) => {
  if (a.length < b.length) {
    return a < b.substring(0, a.length) ? -1 : 1
  } else if (a.length > b.length) {
    return a.substring(0, b.length) <= b ? -1 : 1
  } else {
    return a < b ? -1 : 1
  }
}

export default class CssModulesSassLoader {
  constructor(root, plugins) {
    this.root = root
    this.sources = {}
    this.traces = {}
    this.importNr = 0
    this.core = new Core(plugins)
    this.tokensByFile = {}
  }

  fetch(_newPath, relativeTo, _trace) {
    let newPath = _newPath.replace(/^["']|["']$/g, ''),
        trace = _trace || String.fromCharCode(this.importNr++)

    return new Promise((resolve, reject) => {
      let relativeDir = path.dirname(relativeTo),
        rootRelativePath = path.resolve(relativeDir, newPath),
        fileRelativePath = path.resolve(path.join(this.root, relativeDir), newPath)

      // if the path is not relative or absolute, try to resolve it in node_modules
      if (newPath[0] !== '.' && newPath[0] !== '/') {
        try {
          fileRelativePath = require.resolve(newPath)
        }
        catch (e) {
        }
      }

      const tokens = this.tokensByFile[fileRelativePath]
      if (tokens) {
        return resolve(tokens)
      }

      // fs.readFile( fileRelativePath, "utf-8", ( err, source ) => {
      //   if ( err ) reject( err )
      //   this.core.load( source, rootRelativePath, trace, this.fetch.bind( this ) )
      //     .then( ( { injectableSource, exportTokens } ) => {
      //       this.sources[fileRelativePath] = injectableSource
      //       this.traces[trace] = fileRelativePath
      //       this.tokensByFile[fileRelativePath] = exportTokens
      //       resolve( exportTokens )
      //     }, reject )
      // } )

      sass.render({ file: fileRelativePath }, (err, source) => {
        if (err) reject(err)
        this.core.load(source.css.toString(), rootRelativePath, trace, this.fetch.bind(this))
          .then(({ injectableSource, exportTokens }) => {
            this.sources[fileRelativePath] = injectableSource
            this.traces[trace] = fileRelativePath
            this.tokensByFile[fileRelativePath] = exportTokens
            resolve(exportTokens)
          }, reject)
      })
    })
  }

  get finalSource() {
    const traces = this.traces
    const sources = this.sources
    let written = new Set()

    return Object.keys(traces).sort(traceKeySorter).map(key => {
      const filename = traces[key]
      if (written.has(filename)) {
        return null
      }
      written.add(filename)

      return sources[filename]
    }).join('')
  }
}

from rollup-plugin-postcss.

egoist avatar egoist commented on July 28, 2024

I guess this is not a problem anymore, feel free to re-open if it still doesn't work.

from rollup-plugin-postcss.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.