Anidetrix / rollup-plugin-styles

🎨 Universal Rollup plugin for styles: PostCSS, Sass, Less, Stylus and more.
https://anidetrix.github.io/rollup-plugin-styles
MIT License
243 stars 43 forks source link

code splitting example and docs #130

Open mnlbox opened 4 years ago

mnlbox commented 4 years ago

Hi @Anidetrix and thanks for starting this plugin. You told in ReadMe file that one of the reasons for this plugin is supporting code-splitting:

Complete code splitting support, with respect for multiple entries, preserveModules and manualChunks

Is it supporting now or it's in your roadmap? If yes I think some examples and docs about this can be useful.

mnlbox commented 4 years ago

I want to test the code splitting and multi entry with your package. After switching to your library rollup didn't create any output file at all without any error message but when I mix your plugin with rollup-plugin-sass like bellow it's working:

import sass from "rollup-plugin-sass";
import nodeSass from 'node-sass'
import postcssSmartAsset from "postcss-smart-asset"
import styles from "rollup-plugin-styles"

const postcssSmartAssetOpts = {
  url: 'inline'
}

plugins: [
      sass({
        runtime: nodeSass,
        insert: true
      }),
      styles({
        plugins: [postcssSmartAsset(postcssSmartAssetOpts)]
      }),
]
...

Any idea about this?

Anidetrix commented 4 years ago

Hi @mnlbox,

Is it supporting now or it's in your roadmap?

Yes, it is fully supported. And works just like Rollup's code splitting does, based on dynamic imports and preserveModules/manualChunks options. Also, you need to use extract mode, otherwise CSS is stored inside JS and code splitting occurs on JS side:

styles({
  mode: "extract",
  // other stuff...
})

If yes I think some examples and docs about this can be useful.

Yes, probably should do that.

As for your example:

  1. in this case rollup-plugin-sass just takes all the work and this plugin is not even used.
  2. asset handling functionality is built-in inside the plugin, so postcss-smart-asset plugin is not used in this case, or even might hurt the result, but if you really want to use it, disable the internal asset handler using url: false:
styles({
  mode: "extract",
  url: false,
  plugins: [postcssSmartAsset(postcssSmartAssetOpts)]
})

As for the code splitting itself, just tested it to be sure nothing broke over time, and it works just fine, the simplest test is:

index.js:

import("./style1.scss");
import("./style2.scss");

style1.scss:

.style1 {
  color: red;
}

style2.scss:

.style2 {
  color: blue;
}

If you think something is broken after reading/trying the above, please make a minimal reproduction repo using rollup's REPL, REPL.it, or just a simple GitHub repo.

mnlbox commented 4 years ago

@Anidetrix Oh, thanks, everything is working fine. I also completely remove postcss-smart-asset and using your plugin for asset handling. Just one question. Now I'm using bellow setting and I'm imported scss files in my component but when my component build my CSS import was removed. I also don't want to use inject mode. I won't to use extract but keep my @import url.

styles({
    minimize: true,
    mode: 'extract',
    url: {
      inline: true
    }
  }),

I have this line in my js source file:

import './index.scss'

and want to convert this to something like this after build:

t=require("./index-09905e20.css")

but now it's removed and because of this my component redreded without any styles. Is it normal to remove scss import from js file?

Anidetrix commented 4 years ago

Is it normal to remove scss import from js file?

In extract mode - this is intentional, since there's no require in the browser. I'd imagine you would use it with something like @rollup/plugin-html, or just include the resulting CSS file yourself in your own HTML file. If you need to use CSS as a string inside JS, you can use named css export, as it is described in the docs:

// Using named export of CSS string
import { css } from "./style.css";

If there's a real use case for keeping the require statement in the final bundle, please tell me and we'll go from there.

mnlbox commented 4 years ago

@Anidetrix I don't understand your guide :thinking: I didn't use plugin-html now and I think it's not helpful for my project. Look this is my source structure:

├── FirstComponent
│   ├── index.scss
│   └── index.tsx
├── SecondComponent
│   ├── index.scss
│   └──  index.tsx

So each component has its own style and imported like as I told for example in /FirstComponent/index.tsx I have this line:

import './index.scss'

and this is the style related to this specific component But now because your plugin will remove this import, then my component styles gone and component breaks. :thinking: In code splitting output I need this structure:

├── FirstComponent
│   ├── index.css
│   └── index.js
├── SecondComponent
│   ├── index.css
│   └──  index.js

Now the structure is OK but relation between css and js files removed.

mnlbox commented 4 years ago

@Anidetrix I think it's not documentation :thinking: Maybe we need a fix to prevent remove import.

Anidetrix commented 4 years ago

@mnlbox imports are not kept since they are not JS, thus rollup cannot understand and process them properly. I guess this is not that clear, so this needs documentation and separate feature addition to add require without breaking rollup. So this is labeled as both documentation and enhancement accordingly.

mnlbox commented 4 years ago

@Anidetrix But I didn't have a similar issue before when I used rollup-plugin-postcss and my CSS import line not removed from my js files before. So I think it's not related to rollup :thinking:

mnlbox commented 4 years ago

@Anidetrix I still think it's a bug. Maybe related to these two issues: https://github.com/Anidetrix/rollup-plugin-styles/issues/120 https://github.com/Anidetrix/rollup-plugin-styles/issues/117

I also tried to change import './index.scss' to require("./index.scss") in my index.tsx file. Now this line not removed from final files but the issue is that rollup-plugin-styles plugin didn't create any .css files at all and it seems broken. I don't know it's a bug or something missed in documentation especially in preserveModules: true and extract mode.

dsluijk commented 3 years ago

I also don't want to use inject mode. I won't to use extract but keep my @import url.

I'm also running into this issue.

In extract mode - this is intentional, since there's no require in the browser.

@Anidetrix There is a use-case here, when using downstream bundlers. I'm using Rollup as an intermediate step for an UI library (which I think @mnlbox is also doing, judging from his filenames). While the browser can't recognize import statements, downstream bundlers can. It would be very useful if the import can stay in the bundle, so the end user of my library won't have to import the CSS separately.

You can view my project here, if you want to view for yourself. Run yarn && yarn build in the main directory, and yarn && yarn serve in the example directory. Then go to http://127.0.0.1:1234/ to view the components without styling.

I understand this is not always useful to have. So I propose to add an setting like preserveImports which, when true, keeps the imports in the bundle.

dsluijk commented 3 years ago

I took a shot at fixing it myself, but got stuck when actually writing the import statement. @Anidetrix can you help out here? I did find the output JS files, but the CSS files (and more importantly, their paths) were nowhere to be found.

Anidetrix commented 3 years ago

@dsluijk Returning to work after a long break, will look into it.

dsluijk commented 3 years ago

That's great to hear! If you need any help let me know!

9-lives commented 2 years ago

@dsluijk Returning to work after a long break, will look into it.

It's been a long time since you look into it.

CMcgarty-Wood commented 2 years ago

Any updates on this?

SunHuawei commented 6 months ago

Great to see that I'm not alone.

I left a comment here, and then found this page. It seems like the same topic, and since here are more people, so I just copied the comment here.

I'm in the same situation. I have source files in this structure

- src
    - components
        - button
            - index.js
            - index.scss
    - index.js
    - index.scss

I expected the dist files structure

- dist
    - components
        - button
            - index.js
            - index.css
    - index.js
    - index.css

And in dist/components/button/index.js, the CSS file import statement should be kept.

import './index.css';

And the name should be rather than import './index.scss.js';

I drafted a plugin to explain my idea, here is the codesandbox.

The file name is index.css rather than index.scss.js.

image

The content is plain CSS rather than JS.

image

Here is the main code

function scss() {
  return {
    name: "rollup-scss-to-css-plugin",
    transform(code, id) {
      if (id.endsWith(".scss")) {
        return new Promise((resolve, reject) => {
          const opts = {
            data: code,
            file: id,
            includePaths: [path.dirname(id)],
          };

          sass.render(opts, (error, obj) => {
            if (error) {
              reject(error);
              return;
            }

            this.getModuleInfo(id).meta.targetCSS = obj.css.toString();

            resolve({
              moduleSideEffects: "no-treeshake", // I have to use this to keep the chunk
              code: "export default {}", // fake code to keep chunk
              map: null,
            });
          });
        });
      } else {
        return Promise.resolve({ code, map: null });
      }
    },
    renderChunk(code, chunk, options, meta) {
      if (code.includes(".scss.js")) {
        return code.replace(".scss.js", ".css"); // dirty way to replace the code in consumer files
      } else if (chunk.fileName.endsWith(".scss.js")) {
        options.sourcemap = false; // the sourcemap is not able to be fixed, just sacrifice it
        chunk.fileName = chunk.fileName.replace(".scss.js", ".css"); // mutate this to fix the file name
        return this.getModuleInfo(chunk.facadeModuleId).meta.targetCSS; // replace the content with the plain css
      }
    },
  };
}

I'm not familiar with Rollup's mechanism, but I believe there must be a better way to achieve this. Anyone who has an idea, please let me know.