Postcss-import problem with TailwindCSS v1.0

I know there is an ember-cli-tailwind addon that uses pre-v1.0 tailwindcss. But I want to try to use the version 1 of tailwindcss with ember 3.10. Can anybody share the process of using tailwindcss v1.0 in my new ember 3.10 app without waiting for the update for ember-cli-tailwind?

I am currently trying tailwindcss in ember 3.10. I did the following:

$ ember install ember-cli-postcss
$ npm install tailwindcss --save-dev
$ npm install postcss-import --save-dev

and edited my .ember-cli-build.js:

const purgecss = require('@fullhuman/postcss-purgecss');

module.exports = function(defaults) {
let app = new EmberApp(defaults, {
    postcssOptions: {
      compile: {
        plugins: [
          require('postcss-import'),
          require('tailwindcss')('./config/tailwind.config.js')
        ]
      }
    }
  });

in my app/styles.css:

@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

and I am receiving the following error:

Build Error (PostcssCompiler)

Failed to find 'tailwindcss/base'
  in [
    C:\Users\USER\AppData\Local\Temp\broccoli-14740P3v3ddttxRvl\out-364-broccoli_merge_trees_full_application\app\styles
  ]

How should I do my import statement? @ef4, do you have some advice that you can give? I suspect this is a problem with postcss-import not being able to detect the base.css and the rest of the css files in the node_modules\tailwindcss folder…

This is why I tend to recommend to authors of addons like ember-cli-tailwind that they make the underlying library (tailwind) a peerDependency that gets added via a blueprint, rather than a dependency. In that case you would be free to upgrade tailwind and still use ember-cli-tailwind.

Manually getting tailwind’s files into your app is going to require some broccoli code. Something like:

// Disclaimer, this is just a sketch of the general shape of the solution.
// Probably this doesn't work and you'll need to debug the specifics.

let mergeTrees = require('broccoli-merge-trees');
let Funnel = require('broccoli-funnel');

app = new EmberApp(defaults, {
  trees: {
    styles: mergeTrees([
      'app/styles', 
      new Funnel(require.resolve('tailwindcss'), { 
        include: ['some-parts-of-tailwind'],
        destDir: 'tailwindcss'
      })
    ])
  }
})

(This is all rather unsatisfactory of course, which is why automatic import of styles from NPM is part of the Embroider spec that we’re trying to ship to make things better.)

Really your quickest path to trying tailwind 1.0 without debugging the code above is to fork ember-cli-tailwind and make your app point directly at the fork on github.

This is why I tend to recommend to authors of addons like ember-cli-tailwind that they make the underlying library ( tailwind ) a peerDependency that gets added via a blueprint, rather than a dependency . In that case you would be free to upgrade tailwind and still use ember-cli-tailwind .

I do have plans to update ember-cli-tailwind to prevent this sort of thing from happening in the future, though the last time we had this discussion it was for pretender in the context of ember-cli-mirage, and the conclusion there (I thought) was for ember-cli-mirage to make pretender a dependency, and let host apps override it just by specifying a version themselves and letting npm’s algo do its thing.

I still do not see peerDependencies being used basically anywhere in the Ember ecosystem, so I’d really love to have some unified advise from you and other framework / core team members. I’ve spent a lot of time trying to understand this stuff, and the fact that I’m still unsure leaves me to think many others are in the same boat.

2 Likes

We are constrained by what NPM can do.

was for ember-cli-mirage to make pretender a dependency, and let host apps override it just by specifying a version themselves and letting npm’s algo do its thing.

I don’t remember the exact details we discussed and I can’t find the conversation right now. But this isn’t really a thing that works reliably.

I can’t speak for everybody else, but I think peerDeps are the only viable path offered to us by NPM for these use cases. NPM’s concept of dependencies is fundamentally internal. When using dependencies, you’re creating your own little bubble to install packages into, and no other package in the system can expect to see theme, and you likewise cannot expect to see anybody else’s packages. Even if you happen to depend on a range that overlaps the range somebody else is using.

I still do not see peerDependencies being used basically anywhere in the Ember ecosystem

See https://emberobserver.com/code-search?codeQuery=peerDependencies&sort=score&sortAscending=false

1 Like

I believe it was this convo where I concluded to just use dependencies and let auto import do its thing. But maybe I was getting lost in the discussion and thought you meant to use dependencies rather than peerDependencies.

See https://emberobserver.com/code-search?codeQuery=peerDependencies&sort=score&sortAscending=false

Fair enough, but in this case shouldn’t things like ember-source be a peerDependency of Ember Animated’s package.json? It still seems like the vast majority of peerDependencies are still being encoded as dependencies in the ecosystem.

I’m also often confused by when a dependency should be promoted to the peerDependencies hash. In the original version of ember-cli-tailwind I conceived of tailwindcss as being an implementation detail, but now realizing that tailwindcss will move fast enough, ember-cli-tailwind should really be more of a wrapper library and delegate that out to the host app. But in the case of Pretender and Mirage, the story is not so clear.

1 Like

@ef4, @samselikoff, Turns out there is a problem with postcss-import or broccoli that may need to be resolved:

I have adopted the following solution for the meantime which avoids the file not found problem: ember-cli-build.js

compile: {
        plugins: [{
            module: require('postcss-import'),
            options: {
              path: ['node_modules']
            }
          },
          require("tailwindcss")("./config/tailwind.config.js")
        ]
      },

instead of merely using:

require('postcss-import'),

only

@samselikoff sorry I had to try and experience making this happen without using ember-cli-tailwind for purposes of starting learning how to do it and eventually making me more capable of helping in some other ways in the ember ecosystem. I do am paying attention to the convo of you and @ef4 coz I am still not clear if @samselikoff is saying that I use ember-cli-tailwind and upgrade the tailwind from there. Of which am not yet clear how to go about :stuck_out_tongue:

Currently my installation of tailwindcss v1.0 in ember 3.10 with postcss-import is working but is very much interested in how to go about making a blueprint for this instead. Would love to hear some directions if you have time @samselikoff & @ef4 :slight_smile:

Yes, that issue makes sense.

Before it was working “by accident”, because the broccoli temporary directories just happened to be able to resolve the project’s node_modules most of the time. But that was never intentional, and it actually caused a lot of pain to have tmp inside the project (it generates a ton of extra work for any tool that watches for changes, like IDEs and compilers).

Now tmp is moved into the real system temp folder, so files in tmp can’t resolve things out of node_modules.

The easiest solution would be configuring postcss (or whatever of its plugins does the actual resolving) with an explicit base path pointing at the project. Presumably that is what the path argument you showed is doing. That’s good.

1 Like

Just now I also wanted to try tailwind 1.0, and I followed their own instructions for ember and it worked fine out of the box.

I think it’s necessary to use the special @tailwind directive as the instructions say:

@tailwind base;
@tailwind components;
@tailwind utilities;

Rather than the @import form. The tailwindcss postcss plugin understand that and does the right thing.

2 Likes

I was pleasantly surprised to see that it even works with the purgecss plugin for postcss:

  1. yarn add --dev ember-cli-postcss tailwindcss @fullhuman/postcss-purgecss

  2. Edit ember-cli-build.js:

     'use strict';
    
     const EmberApp = require('ember-cli/lib/broccoli/ember-app');
     const { join } = require('path');
    
     module.exports = function(defaults) {
       let app = new EmberApp(defaults, {
         postcssOptions: {
           compile: {
             plugins: [
               require('tailwindcss'),
               require('@fullhuman/postcss-purgecss')({
                 content: [
                   join(__dirname, 'app', 'index.html'),
                   join(__dirname, 'app', 'templates', '**', '*.hbs'),
                 ],
                 defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || []
               })
             ]
           }
         }
       });
       return app.toTree();
     };
    
  3. Edit app.css:

    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    

With this it’s detecting all the tailwind classes I use in templates and in index.html, and stripping out everything else.

I haven’t gone deep into any of the tailwind customization features, but the basics seem to all be OK.

2 Likes

@ef4,

is it okay to do the following for the content instead?

         content: [
              './app/**/*.html',
              './app/**/*.hbs'
            ],

?

@ef4,

The reason I am using @import is because I need to use ember-modal-dialogs css files in my app/styles/app.css:

@import "ember-modal-dialog/ember-modal-structure.css";
@import "ember-modal-dialog/ember-modal-appearance.css";
@import "tailwindcss/base";
@import "custom-base";
@import "tailwindcss/components";
@import "custom-components";
@import "tailwindcss/utilities";
@import "custom-utilities";
@import "tailwindcss/screens";

and I am using postcss-import in ember-cli-postcss to do it: ember-cli-build.js

const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const { join } = require('path');

module.exports = function(defaults) {
  let app = new EmberApp(defaults, {
    postcssOptions: {
      compile: {
        plugins: [{
            module: require('postcss-import'),
            options: {
              path: ['node_modules']
            }
          },
          require("tailwindcss")("./config/tailwind.config.js"),
        ]
      },
      filter: {
        enabled: false,
        plugins: [
          require('@fullhuman/postcss-purgecss')({
            content: [
              join(__dirname, 'app', '**', '*.html'),
              join(__dirname, 'app', '**', '*.hbs')
            ],
            defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || []
          })
        ]
      }
    }
    });
  return app.toTree();
};

Have you tried changing the @import for the 3 Tailwind lines to @tailwind? The others should stay as @import, but I believe Tailwind needs the special function.

It depends what postcss does with those arguments, but my guess is that they’re relative to the current working directory. So they will work as long as people type ember at the project root. But won’t work if you happen to invoke the ember command from a subdirectory of your project.

1 Like

So far @import "tailwindcss/base";etc loads things up. I don’t know if I am missing something but https://tailwindcss.com/docs/installation/#2-add-tailwind-to-your-css do seem to imply its okay