Skip to content
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

Support and document how to publish modules using Linaria to NPM #178

Open
1 of 2 tasks
satya164 opened this issue Nov 24, 2017 · 14 comments
Open
1 of 2 tasks

Support and document how to publish modules using Linaria to NPM #178

satya164 opened this issue Nov 24, 2017 · 14 comments
Labels
cat: documentation 📖 Work focused on writing documentation cat: improve publishing 🚢 Issues about improvement of publishing libs with Linaria enhancement: approved ✅ Improvement of the current behaviour that has been approved status: up for grabs 🙏 research is done and issue is ready to be grabbed

Comments

@satya164
Copy link
Member

satya164 commented Nov 24, 2017

Background

Linaria is probably the best option to choose among CSS in JS libraries if you are making a component library which you want to publish on NPM.

  • It produces vanilla CSS files, which means anyone can consume them
  • It doesn't have a runtime overhead, so regardless what solution the user uses for CSS, this is not going to another CSS in JS lib to the mix

Today I was trying to publish one of my libraries to NPM which uses Linaria but hit a couple of roadblocks. I hope we can address them.

Approaches

  • Use single: true and ask users to take care of loading the CSS file
  • Use single: false and require a bundler like webpack to load the CSS files
  • Publish the source to npm and ask users to transpile their node_modules with Babel

Blockers

  • The inserted imports are absolute paths, which make it impossible to share the stylesheets among different machines
  • The class names might not be unique because they are based on developer's folder structure, so if two people named "Dave" have same folder structure, e.g. Users/Dave/projects/components, but have libraries with different names, the class names might clash

Proposal

  • Convert imports paths to be relative to the file, pretty-straightforward (fix: insert relative imports for CSS files and support absolute path in outDir #180)
  • It's impossible to generate globally unique names, but we can have some logic so that the hashes include the package name, for example: instead of src/App.js, we use awesome@version~src/App.js to generate the hash, where awesome is the name of the package and version is the version of package we read from package.json if it exists, this assumes package names are globally unique. This won't work properly in some scenarios where the styles changed but version number didn't (e.g. installing from git), but it's impossible/extremely hard to do it.

Currently, I only need the first one, which should be easy to fix. Appreciate your thoughts.

We also need to document the process of publishing libraries using Linaria to NPM.

@satya164 satya164 added the rfc label Nov 24, 2017
zamotany pushed a commit that referenced this issue Nov 27, 2017
…in outDir (#180)

* fix: insert relative imports for CSS files, #178

* fix: support absolute path in output directory
@zamotany zamotany added this to the v0.5.0 milestone Nov 27, 2017
@zamotany zamotany removed this from the v0.5.0 milestone Dec 14, 2017
@zamotany zamotany added this to the 0.6.0 milestone Jan 8, 2018
@mxstbr
Copy link

mxstbr commented Mar 10, 2018

FWIW I'm just going to chime in here and challenge this statement:

It produces vanilla CSS files, which means anyone can consume them

That is why we even started thinking about making a CSS-in-JS library, which ended in styled-components. We made ElementalUI (which is on npm), but users would have to use webpack and configure it exactly right with the less and css loader so they could use our components, which was a pain.

Instead, styled-components lets you publish components and your users don't have to care about how exactly it is styled: they just import it and are ready to go! If they wanna override it, just add a className and boom, done.

I don't think that assumption ("It generates CSS files so it's great for npm libs!") is true at all, in fact I think it hurts the usability.

@satya164
Copy link
Member Author

satya164 commented Mar 10, 2018

I think styled-components could have been a nice solution to this problem, but there are several other CSS in JS libraries too. Which means that if developers start publishing code using CSS in JS libraries to NPM, it's not going to be only styled-components. Developers are going to use their preferred libraries. And for the users using those libraries, they might get multiple CSS in JS libraries included in their bundles, and even multiple copies of the same CSS in JS library are possible when the libraries use different versions.

In addition to this, there's also the question if multiple CSS in JS libs going to conflict with each other. I have no idea, but it doesn't seem far-fetched.

Even if we solve these issues somehow, for people who do server rendering, they have to configure it to generate CSS for each of the CSS in JS libraries used by their dependencies, which is far more worse than configuring their bundler once. And I'm not sure how it will work if the library you depend upon uses a different version of the same CSS in JS library that you use in your app/from a different dependency. When those libraries upgrade/change their dependency on the CSS in JS library, you also have to update your server rendering code.

If they wanna override it, just add a className and boom, done.

I'm not sure how will it work. Won't they have to worry about insertion order? i.e. whatever styling solutions they use, they need to make sure that their CSS gets injected after the library injects its CSS. Which might be tricky.

No doubt vanilla CSS files are not fully automatic (they are in parcel though) and hence not a great DX, but they avoid all of the above issues. The configuration is one-time and they provide a better UX overall. It's certainly not perfect, but it's the best we have currently.

@mxstbr
Copy link

mxstbr commented Mar 10, 2018

I see where you're coming from but we're working on all of those DX issues with https://github.com/cssinjs/istf-spec and @kitten's github.com/kitten/sweetsour, so interop will soon be the standard for CSS-in-JS libs! CSS files ain't changing anytime soon 😉

Anyways, let's agree to disagree, just wanted to chime in with those thoughts.

@satya164
Copy link
Member Author

I don't think there's anything to disagree here. These are not opinions, but problems which prevent using CSS in JS library.

ISTF is great, and I hope it'll solve most of these issues. But to do that, all CSS in JS libraries need to adopt that. What you're saying is ideal, but not practical right now. It'll happen, but in future. And until that happens, vanilla CSS files are the best we have.

@kitten
Copy link

kitten commented Mar 10, 2018

@satya164 To be fair, not all CSS-in-JS libraries need to adopt ISTF or even the connected pipeline. The important part is that a diverse amount of libraries that might or might not form a majority adpot it.

Btw it'd be great to talk about how we can proceed with Sweetsour/ISTF. I'm planning to use it for React Native first to test out its constraints and practicality.

@mxstbr
Copy link

mxstbr commented Mar 10, 2018

I don't think there's anything to disagree here.
[...] vanilla CSS files are the best we have.

That's exactly where we disagree!

For our needs, a shared component library that's meant to be used by any developer who can run npm install, vanilla CSS (or less or sass or linaria) didn't and still doesn't work. That's why we built styled-components, and it is a better solution than vanilla CSS files for our use case.

I'm not saying styled-components is perfect, don't get me wrong you identified very real pain points above, but with these (temporary) pain points (which we're tackling) it's still a better solution for us than vanilla CSS.

That's why I said that the statement above doesn't ring true for my use case at all:

"Linaria is probably the best option [...] if you are making a component library which you want to publish on NPM"

YMMV of course and you're free to say whatever the hell you want, all I'm trying to get across is that that sentence doesn't make any sense to me after I spent two years trying to get rid of vanilla CSS files in my npm packages 😉

@satya164
Copy link
Member Author

Btw it'd be great to talk about how we can proceed with Sweetsour/ISTF. I'm planning to use it for React Native first to test out its constraints and practicality.

HMU! You know where to find me :D

@zamotany zamotany removed this from the 0.6.0 milestone Sep 20, 2018
@eatsjobs
Copy link

eatsjobs commented Nov 7, 2018

Any news on this?can i publish a lib in es module format just transpiling with babel?

@satya164
Copy link
Member Author

satya164 commented Nov 7, 2018

@eatsjobs need to wait for #227 for this

@eatsjobs
Copy link

eatsjobs commented Nov 7, 2018

@satya164 thanks

@oliviertassinari
Copy link

oliviertassinari commented Mar 24, 2019

@satya164 We will release a stable version of Material-UI v4 in the next few weeks. I want to prepare for v5. We have recently made a developer survey. Aside from people asking for more components and better documentation (something we can fix between v4 and v5), a lot of people are asking for a better customization story, faster components, and a smaller bundle size.
We will probably drop IE 11 in Material-UI v5. Edge's recent move to Chromium should enable a IE 11 to Edge migration on the older Windows platforms. It's not possible with the current Edge architectural decisions. It should be possible going forward with Chromium. So, I would expect IE 11 usage to really fade in a year or two. Linaria could be an incredible fit for Material-UI v5.

If only we can figure a great integration with npm, something that made Material-UI move to CSS-in-JS two years ago. Back at the time, CSS-modules support was hard to configure.

@satya164
Copy link
Member Author

@oliviertassinari that sounds great. I think it'll be good to prioritize a workflow for publishing in the next few months. I'm currently a bit busy due to conf, but I'll get back to it this soon.

chrisbateman added a commit to chrisbateman/linaria that referenced this issue Mar 31, 2019
chrisbateman added a commit to chrisbateman/linaria that referenced this issue Mar 31, 2019
chrisbateman added a commit to chrisbateman/linaria that referenced this issue Mar 31, 2019
chrisbateman added a commit to chrisbateman/linaria that referenced this issue Mar 31, 2019
chrisbateman added a commit to chrisbateman/linaria that referenced this issue Apr 3, 2019
chrisbateman added a commit to chrisbateman/linaria that referenced this issue Apr 3, 2019
@jayu jayu added cat: improve publishing 🚢 Issues about improvement of publishing libs with Linaria cat: documentation 📖 Work focused on writing documentation labels Apr 1, 2020
@jayu jayu added enhancement: approved ✅ Improvement of the current behaviour that has been approved status: up for grabs 🙏 research is done and issue is ready to be grabbed and removed rfc labels Apr 1, 2020
@rakeshpai
Copy link

rakeshpai commented Oct 13, 2020

Just checking to see if there has been any movement on this? I want to create a library of components, and want to know the best way to ensure that consumers only bundle the CSS they need based on the components they've imported. I'm not familiar with Linaria internals, but I'm available for help.

@ignatevdev
Copy link

For anyone who stumbles upon this - here's a brief explanation of all the 3 approaches that @satya164 has mentioned.

I was testing it with a UI module which is compiled with babel and uses Typescript and an application which is compiled via Webpack.

Approach 1

  1. Compile your project as usual using babel
  2. Generate a single css file from sources by using @linaria/cli
  3. Import the css file somewhere in your application and let Webpack bundle it for you

Notes:
Currently @linaria/cli is very raw. I was unable to run it without specifying the full path to the bin and it always generates a nested folder structure and can't generate a single file.

Therefore, the command to extract the css looks like this:
./node_modules/@linaria/cli/bin/linaria.js -o dist/styles 'src/**/*.ts'
And afterwards, to combine all the generated css files into one you can use a find command, like this:
find ./dist/styles -type f -name '*.css' -exec cat {} \; > ./dist/styles.css

Caveats:
The biggest problem of this approach IMO is that by putting all the styles into a single file you remove possibility to code-split your libraries' css inside the application.

This is not a problem for a small lib, but might be crucial for a big lib of components with hundreds of kilobytes of styles combined.

And if you need to link your library to your application and test out the features before releasing them, you'll need to figure out a way to automatically re-run the cli script on every file change.

Approach 2

  1. Compile your project as usual using babel
  2. Generate multiple css files from sources by using @linaria/cli and add require statements to the compiled code
  3. Import your components as usual and let Webpack handle the require of the .css files for your

Notes:
To generate the css files use @linaria/cli like this:
./node_modules/@linaria/cli/bin/linaria.js -o dist/styles --source-root src --insert-css-requires dist/cjs 'src/**/*.ts'

This command will go trough all the .ts files inside the src directory, generate the css files inside the dist/styles directory and then add require statements files inside the files of the dist/cjs directory pointing to the css.

Caveats:
As this approach adds require statements with .css files in them, you'll have to import your library only through Webpack. If you try to import it in node, you'll get an error. This is not a big problem for most of the cases, but for example if you have a SSR in your application and use node-externals to move out the modules from the server js bundle, then your SSR would break unless you whitelist your library.

And the question of how to rebuild the css on file changes still remains.

Approach 3

  1. Copy the sources or compile your project with babel, but without the @babel/preset-env and @linaria.
    It's important that your code will not be touched by those presets, otherwise you wouldn't be able to extract the css.
  2. Make sure that your library is processed with babel and linaria in your application
  3. Import your components as usual, they will work as if they are part of your application sources

Notes:
To make babel and linaria process your library, you should modify your exclude in the webpack.config.js, like this:
exclude: /node_modules[\/\\](?!@example\/ui)/
And modify your linaria.config.js, like this:

module.exports = {
  displayName: true,
  rules: [
    {
      action: require('@linaria/shaker').default,
    },
    {
      test: /node_modules[\/\\](?!\@example\/ui)/,
      action: 'ignore',
    },
  ],
};

Caveats:
Firstly, you'd need to make a smart babel config in your libraries repo, as in our case we needed it to have one set of presets and plugins for storybook, but another for publishing.

Then, you'll need to remember to whitelist your library in your webpack and linaria configs. This is not a problem for an internal library, but most likely isn't an option for a public one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cat: documentation 📖 Work focused on writing documentation cat: improve publishing 🚢 Issues about improvement of publishing libs with Linaria enhancement: approved ✅ Improvement of the current behaviour that has been approved status: up for grabs 🙏 research is done and issue is ready to be grabbed
Projects
None yet
Development

No branches or pull requests

9 participants