Skip to content

Support for exports from postcss plugins (other than ICSS ones) #788

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
ZeeCoder opened this issue Nov 4, 2018 · 19 comments
Closed

Support for exports from postcss plugins (other than ICSS ones) #788

ZeeCoder opened this issue Nov 4, 2018 · 19 comments

Comments

@ZeeCoder
Copy link

ZeeCoder commented Nov 4, 2018

Continuing the discussion here: #645 (comment)

I see that currently adding

:export { key: value; }

works, and when the file goes through the css loader, it can be imported in the JS as a custom export.

However as I've outlined I'd like to export a JSON object, and doing so using CSS is not ideal:

:export { data: '{"key": "value"}' }

When I import this in JS, data is returned as string, with the quotes around it. (Without them it wouldn't pass as valid CSS)

Ideally, I'd want to be able to import a JS object, as outputted by a postcss plugin.

In the "next" branch, there was a time where css-loader would look for postcss plugin messages with the "export" type, but it didn't same to make it in v1. (As outlined here: #645 (comment))

I'm wondering if there's any plan to add support for this in the future?

@alexander-akait
Copy link
Member

alexander-akait commented Nov 6, 2018

@ZeeCoder next will be rewrites on ICSS in near future (i.e. built-in plugins will use ICSS, not messages, messages api will be removed). You can import/export values using ICSS spec.

:export is not support nested key/value because css is not supported nested rules. need discussion about this. Can you provide use case?

@ZeeCoder
Copy link
Author

ZeeCoder commented Nov 6, 2018

Hey @evilebottnawi !

I don't expect :export to support nested values, that's fine.
What I would expect though from the css-loader (and not from ICSS) is that it should allow me in some way to declare my own exports to JS-land.

Simplified use-case:
You have a PostCSS plugin, which analyses your CSS and returns some form of metadata, which is then used by a runtime JS lib.

My Exact use-case:
I have a Container Query library, where I collect all container-query related styles from the stylesheet (rules that are like this for example: @container (width > 200px) { color: blue; }) from which I create a metadata object, which I then pass to a javascript runtime that actually applies these rules, when the conditions apply.

If something is unclear, feel free to ask. 👍
For the time being I might just go with the hacky solution, and use :export to export json as string. 🤷‍♂️

@alexander-akait
Copy link
Member

@ZeeCoder Can you provide PoC or minimum code (gist or create repo)? Describe what you exported and what you expect (in README or here)? More information - best and fast solution 😄

@ZeeCoder
Copy link
Author

All I would need, is a way to make it possible for my PostCSS plugin to declare an export with a js object that could be then imported in the JS file after it also went through the css-loader.

":export" doesn't quite work for me because of two reasons:

  • my plugin will be used without webpack as well, so I don't necessarily want to add an ":export" css declaration when it's not needed.
  • Even if I do hack my json object in as an export by making it a string value, it's still hacky to consume, since after I import it I have to strip the quotations, and then do JSON.parse.

My current solution with ":export"

https://github.com/ZeeCoder/container-query/pull/99/files#diff-e745f42867c62365cbc66287035d1195R167
As you can see I'm exposing metadata by stringifying my js object. Then in the JS file I do:

import { meta } from './App.pcss';

// This is why this solution is hacky. I need to convert the string to the object
const parsedMeta = JSON.parse(meta.slice(1, -1))

// do stuff with parsedMeta

What I would like to be able to do

The postcss plugin would "declare" what it would like css-loader to export:

const plugin = 'my-plugin'

export default postcss.plugin(plugin, (options = {}) => (css, result) => {
     // Code ...     
     const meta = // ...

     result.messages.push({
         type: 'export'
         plugin,
         name: "meta",
         value: meta
     })
})
import { meta } from './App.pcss';

// do stuff with meta (meta is already an object here! 🎉)

This solution is not actually something I came up with, but it was part of the @next branch of css-loader for a while, but it seems like it never made it into v1:
#645 (comment)

Hope that clears things up @evilebottnawi !

@alexander-akait
Copy link
Member

my plugin will be used without webpack as well, so I don't necessarily want to add an ":export" css declaration when it's not needed

It is part of future CSS support inside webpack, we should standardize import and export for CSS. But i agree in you case it is unnecessary add :export and :import. Maybe we can support ICSS and import and export messages.

Even if I do hack my json object in as an export by making it a string value, it's still hacky to consume, since after I import it I have to strip the quotations, and then do JSON.parse.

In this case it is impossible to avoid using JSON.stringify (we should use this function to avoid problem with special characters). What is type you want export? Number? Can you provide example too?

@ZeeCoder
Copy link
Author

But my exact point is that it would not be impossible to avoid JSON.stringify, if css-loader had support for custom exports through postcss messages.

I think I gave you all the examples and code I can, not sure if you've read them?
I'm not sure why you think I want to export a number, "meta" is a js object as I stated in my first comment here, that's why I'm stringifying it at the moment. If it was a number, I could just use css :export and be done with it.

If you don't plan to support this use case, I can close the thread.

I can just use :export to export my stringified js object for the time being, I don't think there'll be a better solution. 🤷‍♂️

@alexander-akait
Copy link
Member

@ZeeCoder

If you don't plan to support this use case, I can close the thread.

I just want to collect as much information as possible to make the next version convenient for everyone, so I ask many questions.

Based on you information looks we need support ICSS and message api, I worry about the second because it allows you to change many things at a very low level and can lead to many errors from other developers.

You need only export? No need import?

@ZeeCoder
Copy link
Author

I personally don't need import, just the ability to export stuff to the end user.

By using postcss plugin messages in theory multiple independent PostCSS plugins could have custom exports if they needed to.
So the following would be possible:

import {result1, result2, result3} from './styles.css';

// here, results can be js objects

I would probably limit this api to only export plain objects and simple values like numbers / strings etc, since as you said otherwise it's a fairly low level thing you give access to which might mess things up in the wrong hands.

@alexander-akait
Copy link
Member

@ZeeCoder hm, it is should be possible right now, we exported data in export.locals (style-loader return this object here const exported = require('style.css')). We can change require on import everywhere inside loader in next version (it is allow tree shaking unused exported values).

@ZeeCoder
Copy link
Author

I'm not sure I understand @evilebottnawi , so with an example, how do I use export.locals?

const plugin = 'my-plugin';

export default postcss.plugin(plugin, (options = {}) => (css, result) => {
     // Code ...     
     // generating meta somehow
     const meta = // ...
  
     // Is this it? 🤔
     export.locals = meta;
})

And then is this how I import the meta?

import meta from './styles.pcss';

☝ Is this how it works?

Does this still work with css modules enabled though?
Since that uses the default export already, that's why I'd like to do a named export instead, like so:

import styles, { meta } from './styles.pcss';

@alexander-akait
Copy link
Member

@ZeeCoder friendly ping, we prepare 2 version and can discussion about this. Problem with named export in style-loader, because style-loader export only locals to developers, we need rewrite style-loader for supporting this

@ZeeCoder
Copy link
Author

ZeeCoder commented Dec 3, 2018

Thanks for the heads-up!

For the time being I'm using the "hack" I've described, exporting variables using the ICSS ":export": ZeeCoder/container-query#99

@alexander-akait
Copy link
Member

@ZeeCoder the main problem in what you want to avoid using :export, right?

@ZeeCoder
Copy link
Author

ZeeCoder commented Dec 3, 2018

yeah, since it has nothing to do with the css, really.

With :export, the css file acts as an intermediary between my plugin and the JavaScript module that does the import. Ideally I wouldn't have to push this data through the css, but rather generate a js module that already has a custom export.

the other issue with :export is that the json I'm exporting like this becomes a string, including the quotes!
this means that I need to first strip the quotes, then run JSON.parse to finally get the js object I need.

this whole process could be avoided if named exports were supported, so that I wouldn't even need to JSON.stringify / JSON.parse

@alexander-akait
Copy link
Member

@ZeeCoder any ideas how we can implement this? We can support message api for this, but i afraid future compatibility, named export require rewrite style-loader to allow export not only locals. I am glad any helps with this.

@ZeeCoder
Copy link
Author

ZeeCoder commented Dec 3, 2018

I'll set up a reminder to think about this.

I'm not too involved in how loaders work tbh, so I'd have to do some study first before I can advise on what to do.

@alexander-akait
Copy link
Member

@ZeeCoder feel free to feedback

@alexander-akait alexander-akait added this to the 2.1.0 milestone Dec 5, 2018
@alexander-akait
Copy link
Member

@ZeeCoder what do you think if we solve this on postcss-loader side? Why? In future we can replace postcss on more fast solution, because we need parse only @import and url, also css modules should be used through postcss plugin (it is next major release). In theory we can implement message api on postcss-loader side to allow insert somethings or modify. Can you provide repository with PoC and describe in readme what you actually have and what you expected?

@alexander-akait alexander-akait modified the milestones: 2.1.0, 2.2.0 Mar 5, 2019
@alexander-akait
Copy link
Member

Apparently no one is interested in this, it can also be solved by postcss-loader or you can implement loader above css-loader and implement any non standard solution, thanks for issue, feedback welcome

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

No branches or pull requests

2 participants