Skip to content

feat(babel-plugin): add initial draft#32

Open
asantos00 wants to merge 5 commits intomasterfrom
add-babel-plugin
Open

feat(babel-plugin): add initial draft#32
asantos00 wants to merge 5 commits intomasterfrom
add-babel-plugin

Conversation

@asantos00
Copy link
Copy Markdown
Owner

No description provided.


David is doing a great job documenting this whole process and architecture, and thus I'll not repeat myself here. [add-david-mfe-link]()

As part of this initiative, we created a shared library (which works as any other MFE) and only contains the components that are shared across products. All of this lives on a single repo, and we're using Webpack's Module Federation to define this clear interface between products.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably introduce the MFE meaning before when you mention Micro-frontends. People may not read my articles, and thus wouldn't automatically associate that

However, some challenges came out of this, and one of them is the one I'm sharing here today.
## The challenge

We wanted to import components from a different MFE, and we wanted this to be _code-splitted_, meaning that it should only be loaded when it is needed. We'll refer to this components as **remote components** as they live in a different location.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refer to these --> not this


We wanted to import components from a different MFE, and we wanted this to be _code-splitted_, meaning that it should only be loaded when it is needed. We'll refer to this components as **remote components** as they live in a different location.

Webpack allows to do this by using the _dynamic import_ syntax (`import()`). Here's what we needed to do to load a component that belongs to the **shared** library.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is true for react components only, when importing other data types, there's no need to use the dynamic import

Comment on lines +28 to +36
```js
import React from "react";

const Button = React.lazy(() => import("shared/components/Button"));

const HomePage = () => {
return <Button />;
};
```
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should probably say that this code assumes there's a higher up in the tree


Then, when this `Button` component is used, it will be fetched from the remote source, and rendered to the page. This makes it possible for webpack to code-split our codebase, only downloading the Button when the user needs it.

However, and even thus it works, we though it would be too much of a burden for developers to do. They shouldn't have to care if the component lives in a remote source or not, nor they have to remember of wrapping it in a React.lazy call. All of this would make it very error prone, and at the same time would get our codebase full of `React.lazy` calls.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even though?

return {
visitor: {
ImportDeclaration: function (path, state) {
if (path.node.source.value.startsWith('sharedcomps/')) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mentioned the shared/components/.. before, here you should do the same


We knew that babel-loaders needed to use the `babel-config` `custom` function with a callback, and that they would need to register themselves as a plugin in the global configuration.

As we skimmed through babel-handbook, we also noted that we'd most likely use the visitor pattern, starting by _visiting_ the `ImportDeclarations`, since our initial code was one of them. We decided that we wanted every import starting with `shared` to be a target for this transformation, and thus we added the code to do that.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And we don't want every shared component to do this, only the ones we register in the options

visitor: {
const componentPath = path.node.source.value;

if (path.node.source.value.startsWith('sharedcomps')) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here on this line

visitor: {
ImportDeclaration: function (path, state) {
// console.log('opts', state)
if (path.node.source.value.includes('sharedcomps/Button')) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

```

The above code finds the `ImportDefaultSpecifier` and modifies the initial import declaration so that it keeps all except the default. Then it adds the code to do the dynamic import, together with `React.lazy`.
## Conclusion
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before the conclusion I would add the piece of code that allows us to decide on what components we want this to run on. It's open on a PR for dashboard-ui

@asantos00 asantos00 force-pushed the master branch 3 times, most recently from d45f6c4 to 5271df1 Compare April 1, 2021 11:22
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

Successfully merging this pull request may close these issues.

2 participants