maizzle / framework

Quickly build HTML emails with Tailwind CSS.
https://maizzle.com
MIT License
1.18k stars 47 forks source link

Tailwind problem when working with different folders #781

Closed Frolipon closed 6 months ago

Frolipon commented 1 year ago

Hello,

I encounter a problem that I can't reproduce consistently. Here it is: I configured a repository "production" as follow:

And a second repository "maizzle" which is a fork from https://github.com/maizzle/maizzle

In the repo "maizzle", I have a config.js as follow:

module.exports = {
    prettify: true,
    prettify: {
        indent_with_tabs: true,
      },
    inlineCSS: true,
    removeUnusedCSS: true,
    removeUnusedCSS: {
      removeHTMLComments: false,
      },
    extraAttributes: {
        a: {
            target: '_blank'
        },
        img: {
            border: '0'
        },
        table: {
            border: '0',
            role: 'presentation',
            cellpadding: '0',
            cellspacing: '0'
        },
    },
}

and a config.local.js as follow:

module.exports = {
    path: '../production/clients/client_a/src/',
    build: {
        tailwind: {
            css: '../production/clients/client_a/src/css/tailwind.css',
        },
        templates: {
            source: '../production/clients/client_a/src/templates',
            destination: {
                path: '../production/clients/client_a/build_production',
            },
            assets: {
                source: '../production/clients/client_a/src/images',
                destination: 'images',
            },
        },
    },
}

So the idea is to have a maizzle repository from where we can build htmls from sources in another folder.

When I run run maizzle build local, I sometimes have a very strange behavior in the built HTML: Some of the tailwind commands does not work anymore. For example, class="p-15" will indeed give me a style="padding: 15px;", but a class="py-15" won't work.

I tested several things, and if the files (tailwind.css, templates, layouts and components) in the maizzle repo and the production repo are the same, then the maizzle build local will work smoothly. But if the files in the maizzle repo are the default ones, then the maizzle build local will have the strange behavior described.

Note: The idea behind this is to have a repo for a fork from maizzle to keep upgrading it easily, and without risking any problem with our clients

Frolipon commented 1 year ago

After some more testing, my guess is that Tailwind check the classes present in the maizzle repository, and prepare those classes. But I think it should check the files (layouts, components and template) in the folder described in the config.local.js file.

I tried adding a tailwind.config.js in the production/clients/client_a folder and in the config.local.js file:

tailwind: {
            css: '../production/clients/client_a/src/css/tailwind.css',
            config: '../production/clients/client_a/tailwind.config.js',
        },

And indeed it takes that tailwind.config.js file into account, but I still have the same problem about the classes. If in my production/clients/client_a/src/templates/promotion.html I use a class="py-15" but if this class is not used in the maizzle repository, then Tailwind won't take it into account. And if I add it into the html files in the maizzle repository, then it will be taken into account in the production repository.

Am I missing something in the configuration file? Is it intended? Is it a bug?

cossssmin commented 1 year ago

Hi there,

This is how the JIT engine in Tailwind works, it only generates classes that it 'sees' used in one of the defined content sources. All you have to do is add that path to Tailwind's content key in tailwind.config.js so it can scan files in there for selectors to generate.

Maizzle configures Tailwind CSS with some default content sources (like your layouts/components/templates folders), but for anything else you need to add the paths to your tailwind.config.js. See our docs for more details.

Tip: don't add content paths that contain binary files, like images. It slows down Tailwind a lot because it has to read each file to scan for selectors to generate, and binaries are very expensive to process.

Note that Maizzle v4.2.2 was released yesterday, and it fixes an issue with the default content sources (not directly related to your issue, but I strongly recommend upgrading to it as it comes with performance improvements).

Frolipon commented 1 year ago

Hello,

Thank you so much for your help! I managed to make it work. Now, my next question is: I now have in my tailwind.config.js:

content: [
    '../Production/clients/nose/20220628_Transac_re-design/src/templates/**/*.{html,js}',
    '../Production/clients/nose/20220628_Transac_re-design/src/components/**/*.{html,js}',
    '../Production/clients/nose/20220628_Transac_re-design/src/layouts/**/*.{html,js}',
],

Is there a way to parametrize this path? I would like to keep the writing of the path in one file if possible. is there any way to use the path define in my config.local.js:

module.exports = {
    path: '../production/clients/client_a/src/',
}

Or the define this path still elsewhere and use the variable in both the config.local.js and tailwind.config.js ?

cossssmin commented 1 year ago

You could require config.local.js in tailwind.config.js and use that path:

// tailwind config
const localConfig = require('./config.local.js')

module.exports = {
  content: `${localConfig.path}{layouts,templates,components}/**/*.{html,js}`
}

Mind the / at the end of the path in your local config, its presence determines how you write that content value above.

Btw, as you may have noticed - this should also work for the glob pattern:

content: [
-   '../Production/clients/nose/20220628_Transac_re-design/src/templates/**/*.{html,js}',
-   '../Production/clients/nose/20220628_Transac_re-design/src/components/**/*.{html,js}',
-   '../Production/clients/nose/20220628_Transac_re-design/src/layouts/**/*.{html,js}',
+   '../Production/clients/nose/20220628_Transac_re-design/src/{layouts,templates,components}/**/*.{html,js}',
],
Frolipon commented 1 year ago

Hello,

Unfortunately, I can't make it work. I don't know if I'm writing something wrong here, but it seems that the content definition doesn't work well.

On my side, it looks like:

//tailwind config
const localConfig = require('../../../maizzle/config.local.js')
//the path relative to my config.local.js file

module.exports = {
  content: `${localConfig.path}src/{layouts,templates,components}/**/*.{html,js}`,
(...)
}

The error I get is: ✖ Cannot read properties of undefined (reading 'push') If I change to

//tailwind config
const localConfig = require('../../../maizzle/config.local.js')

module.exports = {
    content: ['../production/clients/client_a/src/{layouts,templates,components}/**/*.{html,js}'],
(...)
}

It works once again, so I assume there is something wrong about the way I use the ${localConfig}

But anyway, this doesn't solve the problem of setting a path in a tailwind.config.js file. Because first it was the path to the layouts, components and css. Now it will be the path to the config.local.js file relative to the tailwind.config.js.

Or am I still missing something?

cossssmin commented 1 year ago

content must be an array, not a string. Your paths are probably not pointing to the correct directory relative to your project root, perhaps this note in the Tailwind docs is relevant:

Paths are relative to your project root, not your tailwind.config.js file, so if your tailwind.config.js file is in a custom location, you should still write your paths relative to the root of your project.

Frolipon commented 1 year ago

Indeed, I saw that I had to point to the root directory. But I don't get how to pass the localConfig var to content array. From what I saw in the note, we can only pass strings or patterns.

Sorry if this is obvious, I'm from the email world without much technical knowledge, but I'm trying to improve that!

cossssmin commented 1 year ago

Sorry about that, I shortened the code for the example.

You need to get the correct key from config.js and then you can pass it to content via a template literal:

// In your tailwind.config.js

// First, get the Maizzle config
const localConfig = require('./config.local.js')

// Pass the `build.templates.destination.path` variable from Maizzle config to Tailwind's `content`
module.exports = {
  content: `${localConfig.build.templates.destination.path}/{layouts,templates,components}/**/*.{html,js}`
}

Mind the added / before {layouts...

cossssmin commented 1 year ago

Just saw this in Tailwind 3.2 which may be exactly what you need:

https://github.com/tailwindlabs/tailwindcss/pull/9396

I’ll try to publish a release today which will include Tailwind CSS 3.2, just need to run a few more tests 👍

Frolipon commented 1 year ago

That seems indeed to resolve the problem :-)

I would be happy to test it once it is in the new release!

cossssmin commented 1 year ago

It's been released in v4.2.0, give it a try.

Frolipon commented 1 year ago

Hello,

I am on v4.3.0 at the moment, but I have a new problem: Every time I try to build the html, I have the following error:

Error:` Failed to find 'tailwindcss/components'
  in [
    /xxx/production/clients/client_a/src/css
  ]

If I install the nodes_module in the client_a folder, then it work. But the whole point of this configuration is to avoid that, and have only the node_modules and the maizzle framwork installed in the maizzle repository.

I'm pretty sure it worked a few days ago with the same config (I need to test it with and older npm version).

I tried playing with the

// In tailwind.config.js
content {relative: true,}

but I couldn't make it work.

I also tried with the 
/*  in tailwind.css */
@config "../../tailwind.config.js"

Do you know if some changes were made?

cossssmin commented 6 months ago

Support for @config will be fixed in Maizzle 5, already got it working locally 👍