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

Module Federation with Tailwind #9784

Closed
Vevl opened this issue Apr 11, 2022 · 11 comments
Closed

Module Federation with Tailwind #9784

Vevl opened this issue Apr 11, 2022 · 11 comments
Assignees

Comments

@Vevl
Copy link

Vevl commented Apr 11, 2022

Current Behavior

Running dep-graph in the project where modules are imported via module federation, federated modules are not marked as a dependency.
This might be a problem because the Tailwind config file benefits from createGlobPatternsForDependencies and therefore federated modules need to be added with a custom glob pattern.

Expected Behavior

createGlobPatternsForDependencies should work with federated modules.
I just expect that one tailwind config should be enough.

Steps to Reproduce

Just create a module federation demo with nx and add tailwind to your host application.
Try to use tailwind in federated modules or its child libraries.

Failure Logs

image

Environment

Nearly the same repo --> https://github.com/Coly010/example-nx-ng-mfe
Angular based mfe's.

@Vevl Vevl added the type: bug label Apr 11, 2022
@Coly010 Coly010 self-assigned this Apr 11, 2022
@Coly010 Coly010 added the scope: angular Issues related to Angular support in Nx label Apr 11, 2022
@Coly010
Copy link
Contributor

Coly010 commented Apr 11, 2022

Hey! This is actually a more involved issue than it would seem on the surface. I'll reply with a more in-depth analysis of the issue 🙂

@Coly010
Copy link
Contributor

Coly010 commented Apr 11, 2022

The issue is around exposed components using any form of global stylesheet.

If a component in a remote application uses classes from a global stylesheet, then that global stylesheet only exists on the remote application's location, unless the same classes/stylesheet are used in the host application.

With a static file, this is easier to handle, as you can copy the stylesheet over to the host application and deploy it with the host application.

However, with any UI library (TailwindCSS) or CSS setup that uses the Purge PostCSS plugin, then this becomes a more involved issue, as the generated CSS bundle for the host application will only look at direct dependents.

Even if we update the remotes to be implicit dependencies of the host to allow the helper to find the files to collect the correct tailwind classes, we have another issue.

The idea behind the host application is that it only gets deployed when required. Once at the start, then anytime a shared dep between it and a remote application changes.

However, a remote app could change, use a new Tailwind class, rebuild and be redeployed, independently from the host application. The host application never rebuilds in this scenario to include the new Tailwind class in the CSS bundle.

It's a known issue with Module Federation itself without a good solution right now. The maintainer of the plugin commented on this same issue in a discussion on the webpack repo here: module-federation/module-federation-examples#714 (reply in thread)

Unfortunately, there isn't much we can recommend beyond trying to stick to component-level styles, shared component libraries and being vigilant of this gotcha.

I'll add the blocked with third party tag to this issue. I'll let it sit for some time while we assess the situation but if no good solution comes for this, I'll close the issue.

@sdedieu
Copy link

sdedieu commented May 6, 2022

I also notice that my remotes apps lose their style appearance if the Tailwind classes are in the HTML files.
But if the Tailwind classes are in the css component files with the @apply directive, they don't.

It is a poor workaround as using the @apply directive isn't considered as a good practice unless you're forced to, and using it everywhere in your remote files kinda make Tailwind loosing its benefits. 😕
But when module federation will support that, you'll just have to move back your Tailwind classes into your HTML files.

@Coly010
Copy link
Contributor

Coly010 commented May 6, 2022

Yes, because Angular will bundle the CSS for that component in the JS bundle, which is fetched correctly.

We know it’s poor practice with Tailwind, but it’s a limitation from Module Federation currently

@bressanelle
Copy link

hey @sdedieu. I guess I found a way to workaround this situation without using apply in each component.

I created tailwind.config.js in root level of monorepo like that

const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind');
const { join } = require('path');

module.exports = {
  darkMode: 'class',
  content: [
    join(__dirname, 'apps/frontend/*/src/**/!(*.stories|*.spec).{ts,html}'),
    ...createGlobPatternsForDependencies(__dirname),
  ],
  theme: {
    extend: {},
  },
  plugins: [require('@tailwindcss/forms')],
};

then I removed of my shell and remote apps all styles using

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

and I also removed their own tailwind.config.js

So I created a lib called ui/common with globalTailwind.css that uses tailwind config

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

and finally added in shell and remote apps in their project.json
"styles": ["libs/ui/common/styles/globalTailwind.css"],

I dont know if it is a elegant method to solve this problem but now my remotes apps and shell are using tailwind utilities classes in their html files. Dark theme also works

@sdedieu
Copy link

sdedieu commented Jun 16, 2022

hey @sdedieu. I guess I found a way to workaround this situation without using apply in each component.

I created tailwind.config.js in root level of monorepo like that

const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind');
const { join } = require('path');

module.exports = {
  darkMode: 'class',
  content: [
    join(__dirname, 'apps/frontend/*/src/**/!(*.stories|*.spec).{ts,html}'),
    ...createGlobPatternsForDependencies(__dirname),
  ],
  theme: {
    extend: {},
  },
  plugins: [require('@tailwindcss/forms')],
};

then I removed of my shell and remote apps all styles using

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

and I also removed their own tailwind.config.js

So I created a lib called ui/common with globalTailwind.css that uses tailwind config

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

and finally added in shell and remote apps in their project.json "styles": ["libs/ui/common/styles/globalTailwind.css"],

I dont know if it is a elegant method to solve this problem but now my remotes apps and shell are using tailwind utilities classes in their html files. Dark theme also works

Sounds interesting! Will definitely try it soon 🙂 Thanks ! 😃

@Coly010
Copy link
Contributor

Coly010 commented Jun 20, 2022

@bressanelle It should work, but the downside is you lose the benefit of the Tailwind's PostCSS Purge step where it reduces the size of your CSS bundle.

But it is a workaround.

I'm going to close this issue as this is a limitation of the tooling, and there is a workaround listed above.

@stevebrowndotco
Copy link

@Coly010 @Vevl FYI I was able to solve this using https://github.com/ben-rogerson/twin.macro with all the benefits.

@carlflor
Copy link

would you be able to share a sample @stevebrowndotco ?

@pulsematrix
Copy link

I updated tailwind.config.js of my host application to make it work by adding the following lines:

join(__dirname, '../first-remote/src/**/!(*.stories|*.spec).{ts,html}'),
join(__dirname, '../second-remote/src/**/!(*.stories|*.spec).{ts,html}'),

It is now explicitly includes the remote applications' source files when scanning for used Tailwind classes.

tailwind.config.js (host application):

const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind');
const { join } = require('path');

module.exports = {
  content: [
    join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'),
    ...createGlobPatternsForDependencies(__dirname),
    join(__dirname, '../first-remote/src/**/!(*.stories|*.spec).{ts,html}'), // added
    join(__dirname, '../second-remote/src/**/!(*.stories|*.spec).{ts,html}'), // added
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

Tailwind's purge process also checks the source files of first-remote and second-remote for any used classes. This way, when the host application's CSS bundle is generated, it will include the necessary classes from the remote applications, even if they are not direct dependencies.

Keep in mind, though, that this solution may require manual maintenance as you add more remote applications or make structural changes to your project. So, it's still a good idea to follow best practices like using component-level styles and shared component libraries to avoid potential issues in the future.

@github-actions
Copy link

github-actions bot commented May 2, 2023

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 2, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants