Skip to content

Chaining scss and postcss produces unexpected results #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
robinloeffel opened this issue Feb 10, 2020 · 9 comments
Closed

Chaining scss and postcss produces unexpected results #115

robinloeffel opened this issue Feb 10, 2020 · 9 comments

Comments

@robinloeffel
Copy link

robinloeffel commented Feb 10, 2020

Describe the bug
When using scss and postcss inside of the preprocess array of rollup-plugin-svelte, PostCSS only seems to perform some, but not all, desired transformations. What I meant by this is that while, for example, font-family: Inter, system-ui; correctly gets transformed to font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;, color: var(--color-text); does not get transformed to color: #fff;. This then results in the app not rendering correctly in IE. https://int.agricontrol.app/svelte/

To Reproduce
This is my rollup.config.js:

/* eslint-env node */

import svelte from 'rollup-plugin-svelte';
import babel from 'rollup-plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import browsersync from 'rollup-plugin-browsersync';
import { scss, postcss, globalStyle } from 'svelte-preprocess';
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';
import cssEnv from 'postcss-preset-env';

const dev = process.env['dev'] === 'true';
const buildOnly = process.env['build-only'] === 'true';

export default {
    input: 'src/app.js',
    output: {
        sourcemap: dev,
        format: 'iife',
        file: 'public/app.js'
    },
    plugins: [
        svelte({
            dev: dev,
            css: css => {
                css.write('public/app.css', dev)
            },
            preprocess: [
                globalStyle(),
                scss(),
                postcss({
                    plugins: [
                        cssEnv(),
                        !dev && autoprefixer(),
                        !dev && cssnano()
                    ].filter(plugin => plugin)
                }),
            ]
        }),
        !dev && babel({
            extensions: [ '.js', '.mjs', '.svelte' ],
            include: [ 'src/**', 'node_modules/svelte/**' ],
            presets: [
                ['@babel/preset-env', {
                    useBuiltIns: 'usage',
                    corejs: 3
                }]
            ]
        }),
        resolve(),
        commonjs(),
        !dev && terser(),
        !buildOnly && browsersync({
            server: 'public',
            notify: false,
            ui: false
        })
    ].filter(plugin => plugin)
};

Then, for example, the style tag of my main Link.svelte:

<style lang="scss">

    a {
        display: inline-block;
        color: inherit;
    }

    .primary,
    .secondary,
    .ghost {
        color: var(--color-white);
        position: relative;
        border-radius: 10px;
        padding: 15px 25px;
        text-decoration: none;
        font-size: .8rem;
        font-weight: var(--font-weight-bold);
        text-shadow: none;

        &::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            border-radius: inherit;
            box-shadow: 0 2px 10px rgba(0, 0, 0, .15);
            opacity: 0;
            transition: opacity .3s ease-in-out;
        }

        &:hover {
            &::after {
                opacity: 1;
            }
        }
    }

    .primary {
        background-color: var(--color-brand);
    }

    .ghost {
        background-color: var(--color-brand-muted);
    }

    .navigation {
        position: relative;
        padding: 30px;
        text-decoration: none;

        &::after {
            content: '';
            position: absolute;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 5px;
            background-color: var(--color-brand-muted);
            transform: scaleY(0);
            transition: transform .3s ease-in-out;
            transform-origin: bottom;
        }

        &:hover {
            &::after {
                transform: scaleY(1);
            }
        }
    }

</style>

My package.json:

{
    "name": "agricontrol",
    "description": "Landing page for Agricontrol",
    "author": "Robin Löffel <[email protected]> (https://robinloeffel.ch)",
    "license": "MIT",
    "homepage": "https://agricontrol.app",
    "repository": "rbnlffl/agricontrol",
    "scripts": {
        "start": "yarn cleanup && rollup -c -w --environment dev",
        "prod": "yarn cleanup && rollup -c -w",
        "dist": "yarn cleanup && rollup -c --environment build-only",
        "cleanup": "rm -rf public/app.*"
    },
    "devDependencies": {
        "@babel/core": "^7.8.4",
        "@babel/preset-env": "^7.8.4",
        "@rollup/plugin-commonjs": "^11.0.2",
        "@rollup/plugin-node-resolve": "^7.1.1",
        "autoprefixer": "^9.7.4",
        "core-js": "^3.6.4",
        "cssnano": "^4.1.10",
        "node-sass": "^4.13.1",
        "postcss": "^7.0.26",
        "postcss-preset-env": "^6.7.0",
        "regenerator-runtime": "^0.13.3",
        "rollup": "^1.31.0",
        "rollup-plugin-babel": "^4.3.3",
        "rollup-plugin-browsersync": "^1.1.0",
        "rollup-plugin-svelte": "^5.1.1",
        "rollup-plugin-terser": "^5.2.0",
        "svelte": "^3.18.1",
        "svelte-preprocess": "^3.4.0"
    },
    "browserslist": [
        ">1%",
        "not dead"
    ]
}

Expected behavior
What I expect to happen is for scss to perform the transpiling, like resolving nestings and such, and then for postcss to perform the polyfilling.

Information about your project:

  • Your browser and the version: Firefox Developer Edition, v73.0

  • Your operating system: macOS 10.15.3

  • svelte-preprocess version: 3.4.0

  • Whether your project uses Webpack or Rollup: Rollup, v1.31.0

@kaisermann
Copy link
Member

kaisermann commented Feb 10, 2020

Hey @rbnlffl 👋 Just tried your configs in the example/svelte-rollup project and got it working. Here's a simplified repro: template.tar.gz

image

Can you test it?

@robinloeffel
Copy link
Author

Thanks for taking the time, man! Your example works.

After some investigation, I know where the problem lies—it's the globalStyle preprocessor. I have the following App.svelte, which I use for some global variables and basic styling:

<style global lang="scss">

    :root {
        --color-white: #fff;
        --color-black: #000;
        --color-shadow: rgba(0, 0, 0, .1);
        --color-text: #606060;
        --color-brand: #67bf66;
        --color-brand-muted: #67bf669a;
        --font-weight-light: 300;
        --font-weight-regular: 400;
        --font-weight-bold: 600;
    }

    *,
    *::after,
    *::before {
        box-sizing: border-box;
    }

    html {
        font-family: Inter, system-ui;
        font-size: 110%;
        line-height: 1.6;
        color: var(--color-text);
    }

    body {
        margin: 0;
    }

    #app {
        display: flex;
        flex-direction: column;
        align-items: center;
    }

</style>

Now, when I run my code as I did before, the ouput for, let's say html, is:

html {
    font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;
    font-size: 110%;
    line-height: 1.6;
    color: var(--color-text);
}

If I move globalStyles() to the last index of the preprocess array in my rollup.config.js, however, the output looks like this:

html {
    font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;
    font-size: 110%;
    line-height: 1.6;
    color: #606060;
    color: var(--color-text);
}

Interesting. But now, lets look at another component, trying to access the variables exposed in App.svelte. For example Link.svelte, the example in my original post.

<style lang="scss">

    a {
        display: inline-block;
        color: inherit;
    }

    .primary,
    .secondary,
    .ghost {
        color: var(--color-white);
        position: relative;
        border-radius: 10px;
        padding: 15px 25px;
        text-decoration: none;
        font-size: .8rem;
        font-weight: var(--font-weight-bold);
        text-shadow: none;

        &::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            border-radius: inherit;
            box-shadow: 0 2px 10px rgba(0, 0, 0, .15);
            opacity: 0;
            transition: opacity .3s ease-in-out;
        }

        &:hover {
            &::after {
                opacity: 1;
            }
        }
    }

    .primary {
        background-color: var(--color-brand);
    }

    .ghost {
        background-color: var(--color-brand-muted);
    }

    .navigation {
        position: relative;
        padding: 30px;
        text-decoration: none;

        &::after {
            content: '';
            position: absolute;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 5px;
            background-color: var(--color-brand-muted);
            transform: scaleY(0);
            transition: transform .3s ease-in-out;
            transform-origin: bottom;
        }

        &:hover {
            &::after {
                transform: scaleY(1);
            }
        }
    }

</style>

The output there is, for example:

.primary.svelte-1lyn5rv, .secondary.svelte-1lyn5rv, .ghost.svelte-1lyn5rv {
    color: var(--color-white);
    position: relative;
    border-radius: 10px;
    padding: 15px 25px;
    text-decoration: none;
    font-size: .8rem;
    font-weight: var(--font-weight-bold);
    text-shadow: none;
}

So I guess the issue is with globalStyles and postcss not knowing how to pass global variables to each other. What do you think? Also, is there some kind of options for globalStyles?

@kaisermann
Copy link
Member

Hmmm, I have to check what's happening. Would you be able to provide a repro with these components + your configs?

Currently, the globalStyles don't accept any kind of options since it's job is only to wrap selectors in :global().

@robinloeffel
Copy link
Author

@kaisermann
Copy link
Member

kaisermann commented Feb 11, 2020

After testing it a bit, it seems the postcss-preset-env doesn't understand very well the :global(...) selectors. I'd suggest adding it to the end of the preprocessors array, as you said or using the auto preprocess mode (which does that for you).

I'll add a note about this to the readme

@robinloeffel
Copy link
Author

Alright, doing this doesn't completely fix the problem, though. While App.svelte correctly gets transpiled to, for example

html {
    font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;
    font-size: 110%;
    line-height: 1.6;
    color: #606060;
    color: var(--color-text);
}

Link.svelte looks like that:

.primary.svelte-1lyn5rv, .secondary.svelte-1lyn5rv, .ghost.svelte-1lyn5rv {
    color: var(--color-white);
    position: relative;
    border-radius: 10px;
    padding: 15px 25px;
    text-decoration: none;
    font-size: .8rem;
    font-weight: var(--font-weight-bold);
    text-shadow: none;
}

For now, I can get arount this by having a globals.scss containing my variables and then importing this file in my Svelte components wherever needed. But the possibility of globalStyles and postcss communicating global variables to each other would be super-dope!

@kaisermann
Copy link
Member

kaisermann commented Feb 11, 2020

That's happening because preprocessed files don't communicate with each other nor have a shared context. Svelte and the preprocessors (postcss) don't know anything about those :root definitions in in your App.svelte when preprocessing Link.svelte. A possible way to workaround this limitation is to run postcss over the generated css file instead.

@robinloeffel
Copy link
Author

I agree—in theory, this should solve it. But sadly, changing my rollup config to the following produces the exact same result as in my previous reply:

/* eslint-env node */

import svelte from 'rollup-plugin-svelte';
import babel from 'rollup-plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import browsersync from 'rollup-plugin-browsersync';
import postcss from 'rollup-plugin-postcss';
import preprocess from 'svelte-preprocess';
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';
import cssEnv from 'postcss-preset-env';

const dev = process.env['dev'] === 'true';
const buildOnly = process.env['build-only'] === 'true';


export default {
    input: 'src/app.js',
    output: {
        sourcemap: dev,
        format: 'iife',
        name: 'agricontrol',
        file: 'public/app.js'
    },
    plugins: [
        resolve(),
        commonjs(),
        svelte({
            dev: dev,
            preprocess: preprocess(),
            emitCss: true
        }),
        postcss({
            extract: true,
            sourcemap: dev,
            plugins: [
                cssEnv(),
                !dev && autoprefixer(),
                !dev && cssnano()
            ].filter(plugin => plugin)
        }),
        !dev && babel({
            extensions: [ '.js', '.mjs', '.svelte' ],
            include: [ 'src/**', 'node_modules/svelte/**' ],
            presets: [
                ['@babel/preset-env', {
                    useBuiltIns: 'usage',
                    corejs: 3
                }]
            ]
        }),
        !dev && terser(),
        !buildOnly && browsersync({
            server: 'public',
            notify: false,
            ui: false
        })
    ].filter(plugin => plugin)
};

@kaisermann
Copy link
Member

There's a possibility that the generated css file is not passing through rollup-plugin-postcss because the svelte-rollup-plugin writes the file directly to the filesystem (https://github.com/sveltejs/rollup-plugin-svelte/blob/master/index.js#L101) instead of emitting it to rollup.

Possibly related PR: sveltejs/rollup-plugin-svelte#72.

You could run postcss AFTER the build step (i.e via cli), which would definitely work 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants