postcss / postcss-custom-properties

Use Custom Properties in CSS
https://postcss.github.io/postcss-custom-properties
MIT License
596 stars 77 forks source link

AppendVariables results in thousands of duplicate entries #102

Closed Vanuan closed 5 years ago

Vanuan commented 6 years ago

I'm using Webpack with postcss loader and extract text plugin. This results in postcss-custom-properties plugin to be called multiple times, for each imported css file.

So when appendVariables is set to true the appended ":root" section is duplicated multiple times in the resulted css. This slows down chrome dev tools and significally increases css file size.

Vanuan commented 6 years ago

As a possible solution I see postcss-custom properties plugin to memoize whether it's the second time call and skip appendVariables logic.

Vanuan commented 6 years ago

A cleaner approach would be just to output a new css file with variables. Something like this:

const postCSSCustomProperties = require('postcss-custom-properties');
const symbolsParser = require('scss-symbols-parser');
...
const toObject = (arr, { keyName, valueName }) => {
        return arr.reduce(
                (object, arrayEl) => Object.assign(object, { [arrayEl[keyName]]: arrayEl[valueName] }), {}
        );
};

const scssVarsToObject = (filename) => {
    const scssParsed = symbolsParser.parseSymbols(
        fs.readFileSync(path.resolve(filename), 'utf8')
    );
    const scssVars = toObject(scssParsed.variables, {
        keyName: 'name',
        valueName: 'value',
    });
    return scssVars;
};

const scssVarsToCssVar = (filename) => {
    const dollarObj = scssVarsToObject(filename);
    return toObject(
        Object.entries(dollarObj).map(
            (el) => [el[0].replace(/^\$/, ''), el[1]]
        ), {
            keyName: '0',
            valueName: '1'
        }
    );
};
...

module: {
    rules: [
        test: /colors\.scss$/,
        use: new ExtractTextPlugin({
            filename: 'colors.css',
            allChunks: true
        }).extract([{
                loader: 'css-loader',
                options: {
                    modules: true,
                    localIdentName: '[name]__[local]___[hash:base64:5]',
                    importLoaders: 1
                },
            },
            {
                loader: 'postcss-loader',
                options: {
                    ident: 'postcss',
                    plugins: [postCSSCustomProperties({
                        appendVariables: true,
                        variables: scssVarsToCssVar('./css/variables/colors.scss'),
                        warnings: true,
                        noValueNotifications: true,
                    })],
                },
            },
        ]),
    ]
}
Vanuan commented 6 years ago

The use case here is to get scss files as a source of variables, convert it to css custom properties definitions and output it somewhere in html or css.

jonathantneal commented 6 years ago

Wow, thanks for the detailed report, @Vanuan . I think the ability to export variables to a file seems very reasonable when PostCSS is running over multiple files.

Some background. It’s been difficult for PostCSS plugin authors (or, at least, me) to feel comfortable creating new files. First; PostCSS plugins try to do one thing and do it well, and that one thing typically involves manipulating the CSS AST. This means creating new files often feels out of scope. Second; there is no convention (that I know of) from Webpack, Gulp, Grunt, etc. that instructs authors as to how our libraries/plugins should distribute supplemental content, such as a css file with just the exported variables. While I’d love to see these bigger questions answered, I don’t want this to stop me from delivering this feature. Let me think.

And while I do that, @ai, @ben-eb, @RyanZim, do you have any ideas or insights?

Vanuan commented 6 years ago

I think there should be some way to emulate import or entry point. So that when extract-text-webpack-plugin (along with postcss-loader) encounters duplicate chunks it doesn't produce duplicate content.

The problem here is not putting variables to separate file but avoiding the duplication.