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

Dynamically import CSS #261

Closed
boris-petrov opened this issue Dec 16, 2019 · 18 comments
Closed

Dynamically import CSS #261

boris-petrov opened this issue Dec 16, 2019 · 18 comments

Comments

@boris-petrov
Copy link
Contributor

I'm trying to achieve the same functionality as dynamically-importing JavaScript (in order to have a smaller bundle size). That is, I have a CSS file that I want to load on-demand. What I do right now is:

ember-cli-build.js:

new Funnel('node_modules/c3', { destDir: '/assets', files: ['c3.css'] }),

app code:

function importCss(url: string): void {
  const link = document.createElement('link');
  link.rel = 'stylesheet';
  link.type = 'text/css';
  link.href = url;
  document.getElementsByTagName('head')[0].appendChild(link);
}

importCss('/assets/c3.css')

Not the best code as you see. I mostly hate the part in ember-cli-build. I guess ember-auto-import can help mostly with it. I read this answer - point 2 seems like something similar to what I need but it doesn't work - I think the browser tries to parse the result file as JavaScript. This PR also doesn't do what I want.

Any suggestions as to what I could do or what should be developed in ember-auto-import in order to support that?

@ef4
Copy link
Collaborator

ef4 commented Dec 17, 2019

I think #205 supports your use case, but you need to bring your own webpack config for it.

let app = new EmberApp(defaults, {
  autoImport: {
    webpack: {
      // In here you need to configure something like webpack's style-loader
    }
  }
});

@boris-petrov
Copy link
Contributor Author

boris-petrov commented Dec 17, 2019 via email

@ef4
Copy link
Collaborator

ef4 commented Dec 17, 2019

Whether the code ends up in vendor vs another bundle is determined by whether anybody is dynamically importing it. Try adding style-loader and do await import('./your/style.css'). I think it may just work.

@boris-petrov
Copy link
Contributor Author

boris-petrov commented Dec 18, 2019

@ef4 - thanks for the support. So here is what I did:

...
module: {
  rules: [
    {
      test: /\.css$/i,
      use: [
        { loader: 'style-loader', options: { injectType: 'linkTag' } },
        'css-loader',
      ],
    },
  ],
},
...

This in the webpack part of autoImport in ember-cli-build. Then I have await import('c3/c3.css');.

The problem is that ember-auto-import (or something, not sure) generates a chunk.xxx.js file which contains the code from style-loader for injecting a link tag and the CSS from the file itself. That's the first problem - the CSS should not be there. The second issue is that then this link is inserted and its href is the whole CSS - i.e. a request is made to a URL which is the CSS contents of the file. I hope you understand what I mean.

If I remove injectType: 'linkTag' then it works fine - a style tag with the contents of the file is created.

So this kind of works for me, but perhaps you could take a look at the other use case (which I also prefer) - the style-loader to append a link tag which contains the link to the CSS file which is bundled somewhere... not sure how this all would work.

Thanks again!

@ef4
Copy link
Collaborator

ef4 commented Dec 21, 2019

I think that falls squarely into picking a different webpack config. If there's an option to style-loader, or a replacement for style-loader, that does what you want, that would be the solution.

(All that said, I do want to add a default standardized CSS configuration for ember-auto-import, so that it follows the same behavior that will be in embroider. But that would be a future feature that we don't have right now.)

@boris-petrov
Copy link
Contributor Author

@ef4 - I'm not sure I understand what you mean. My point was - I made it work "fine" with style-loader, however what I think is best (and style-loader allows it) is for the styles to be inserted as link tags. But this doesn't work when used in ember-auto-import. Check the largest paragraph of my previous message for an explanation why. If I'm not making sense, I could create a reproduction repo for you to check out. I believe that is a bug in ember-auto-import (or perhaps just something which wasn't supposed to work ever).

In any case, I managed to do what I wanted (or almost at least) so this issue can be closed if you want, or you could leave it open until you decide that the other part with the link tag is OK too.

Thank you!

@ef4
Copy link
Collaborator

ef4 commented Dec 23, 2019

Oh, I see. So you already tried that config but we break it somehow. Understood now.

@brunoocasali
Copy link

Reading this thread I could reduce my build size by importing css dynamic, but now I'm facing some strange behaviour, @ef4 and @boris-petrov if you could help me with this, would be awesome...

This is my load css in a component.js:

  *loadScript() {
    yield import('flatpickr/dist/flatpickr.min.css');
    this.script = yield import('flatpickr').then(m => m.default);
  }
...
"css-loader": "3.6.0",
"ember-auto-import": "1.5.3",
"style-loader": "1.2.1",
...

My autoImport config:

    autoImport: {
      alias: {
        flatpickr: 'flatpickr/dist/flatpickr.min',
      },
      webpack: {
        module: {
// to make this work, i must for some reason I don't understand, duplicate this rule.
          rules: [
            {
              test: /\.css$/,
              use: ['style-loader', 'css-loader']
            },
            {
              test: /\.css$/,
              use: ['style-loader', 'css-loader']
            },
          ]
        },
      }
    }

With just one rule I got this error:

ERROR in ./node_modules/flatpickr/dist/flatpickr.min.css 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type.
> .flatpickr-calendar{background:transparent;opacity:0;display:none;text-align:center;visibility:hidden;padding:0;-webkit-animation:none;animation:none;direction:ltr;border:0;font-size:14px;line-height:24px;border-radius:5px;position:absolute;width:307.875px;.......omitted........flatpickr-prev-month{/*
|       /*rtl:begin:ignore*/left:0;/*
|       /*rtl:end:ignore*/}/*
 @ /private/var/folders/f0/c6hyjkv52qzgm01qjspmhlqr0000gn/T/broccoli-14077ALI16vwbmhoj/cache-235-bundler/staging/app.js 26:75-173
 @ multi /private/var/folders/f0/c6hyjkv52qzgm01qjspmhlqr0000gn/T/broccoli-14077ALI16vwbmhoj/cache-235-bundler/staging/l.js /private/var/folders/f0/c6hyjkv52qzgm01qjspmhlqr0000gn/T/broBuild Error (Bundler)

Build Error (Bundler)

webpack returned errors to ember-auto-import

Is this a webpack, or css-loader/style-loader or ember-auto-import issue?

Thanks in advance ;)

@boris-petrov
Copy link
Contributor Author

@brunoocasali - that's the webpack config we use:

{
  test: /\.css$/i,
  use: [
    {
      loader: 'style-loader',
    },
    'css-loader',
  ],
},

Works fine on our side. I guess it should for you too.

@brunoocasali
Copy link

Unfortunately @boris-petrov this not work too, I've got the same error I've reported above :/

@RobbieTheWagner
Copy link

@brunoocasali why not use the addon for flatpickr? https://github.com/shipshapecode/ember-flatpickr

@brunoocasali
Copy link

brunoocasali commented Sep 7, 2020

In my current knowledge @rwwagner90 any addon I add to my ember app, will be bundled and carried all over the place even if the user does not use it in every route it load, am I right? (If I misunderstood please show me the light!)

The ember-flatpicker can do something like that?

I really wanted to give the users only what they need only when they want it!

@wagenet
Copy link

wagenet commented Oct 7, 2020

@pomm0
Copy link

pomm0 commented May 6, 2021

To me this thread is a bit fragmented, so I wanted to give a summary for potential future readers about how I made it work.
I needed it for mapbox-gl (at time of writing version 2.2.0) and wanted to lazily import js and css together:

Needed node packages (ember-auto-import @1.11.3):

npm install --save-dev css-loader style-loader

ember-cli-build.js:

autoImport: {
  alias: {
    'mapbox-gl-css': 'mapbox-gl/dist/mapbox-gl.css',
  },
  webpack: {
    module: {
      rules: [
        {
          test: /\.css$/i,
          use: [
            'style-loader',
            'css-loader',
          ],
        },
      ],
    },
  },
},

Where you want to use it (component.js or wherever):

const [mapboxModule] = await Promise.all([import('mapbox-gl'), import('mapbox-gl-css')])
const mapbox = mapboxModule.default;

There's nothing to do with the promise of import('mapbox-gl-css'), it will inject it as style tag to dom by itself.

The alias is not needed I guess, could also be import via import('mapbox-gl/dist/mapbox-gl.css') but I like the alias.

Edit:

As ef4 pointed out, for version 2.x of ember-auto-import no need to have the webpack part, so the ember-cli-build file would look something like that:

ember-cli-build.js:

autoImport: {
  alias: {
    'mapbox-gl-css': 'mapbox-gl/dist/mapbox-gl.css',
  },
},

@ef4
Copy link
Collaborator

ef4 commented May 6, 2021

I would also point out that ember-auto-import 2.0 (which is in beta release right now) includes style-loader and css-loader by default, so in that case you would remove the whole webpack section of the config above.

@boris-petrov
Copy link
Contributor Author

@ef4 - with style-loader now included by default, how do we add options to it? I have a custom insert function defined that I also want with ember-auto-import 2.0. How do I do that?

@ef4
Copy link
Collaborator

ef4 commented May 25, 2021

We don't currently offer an option to extend the style-loader config, but that would be a reasonable thing to add if you want to make a PR.

@ef4
Copy link
Collaborator

ef4 commented Jul 20, 2021

I think this is all resolved between ember-auto-import 2.0 and #408.

@ef4 ef4 closed this as completed Jul 20, 2021
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

6 participants