module-federation / module-federation-examples

Implementation examples of module federation , by the creators of module federation
https://module-federation.io/
MIT License
5.54k stars 1.73k forks source link

How typesafe can a remote be with Typescript? #20

Closed echarles closed 2 years ago

echarles commented 4 years ago

Update from @ScriptedAlchemy - https://www.npmjs.com/package/@module-federation/typescript has been released

I can see a disadvantage using remotes libraries vs the classical libraries where e.g. typescript can be used to enforce strong types. The current typescript example defines a generic app2/Button which is similar defining a any type.

https://github.com/module-federation/module-federation-examples/blob/cf2d070c7bbb0d5f447cfe33f69e07bcf85f8ddb/typescript/app1/src/app2.d.ts#L3-L7

As the remote var does not have type, I don't see a way to benefit from the app2 types in app1. Any idea on this?

osya commented 2 years ago

federation.config.json

"filename" is not used by federated-types. So "filename" should stay in webpack.config

popuguytheparrot commented 2 years ago

I'm not a TS user so I'm no help here.

Who can discuss on this theme? Maybe RFC or something else? 2y opened issue with common case where answer is "I'm not a TS" look weird

Kjaer commented 2 years ago

I disagree. In the end, Typescript support must not an official thing for this tech. On the other hand, there are plenty of solutions mentioned above works just fine.

adrianbw commented 2 years ago

I do think a good Typescript solution is very important for the long-term success of module federation, given how TS keeps growing in popularity.

Would something like this work?

Package A: The code you want to get through module federation. Package B: A package that imports Package A through module federation and then re-exports those functions with correct typing. Package C: Your application, which adds Package B through npm.

Package A and Package B should be published from the same repo, so that you don't have to keep anything in sync between repos. I'm honestly not sure if this method would preserve the benefits of module federation—what happens when webpack runs on package C?

ckken commented 2 years ago

emp v2 work very very use remote d.ts

sabeekaq commented 2 years ago

Sharing my setup:

For the host which exposes modules

  1. I created a dts-loader which will emit and collect the .d.ts file. And also it generates the entry .d.ts file based on the exposes.
  2. Then I create a tarball(.tgz file) for the emitted types & entries. And I deployed this tarball with the application's statics, for example, to http://localhost:9000/app-dts.tgz

For the host which requires remotes

  1. I created a webpack plugin WebpackRemoteTypesPlugin which will download and unpack the tarball from remote automatically when running webpack.

More details can be found here: https://github.com/ruanyl/dts-loader Comments & feedbacks are welcome :)

I was able to get this working in a polyrepo. One thing that tripped me up pretty hard was your exposes must explicitly reference the files you're exposing.

  exposes: {
    './theme': './src/themes/index.ts',
    './helpers': './src/helpers/index.ts',
    './constants': './src/constants/index.ts',
  }

This will not work:

  exposes: {
    './theme': './src/themes,
    './helpers': './src/helpers',
    './constants': './src/constants,
  }

Opened up this issue so hopefully it can be improved: ruanyl/dts-loader#7

Also, I was able to automate the archiving by using tar-webpack-plugin, so my types are generated and synced whenever I npm start - pretty neat!

Could you please share how you got tar-webpack-plugin to work?

makotot commented 2 years ago

Although I wonder what should be done when running tsc in CI, I think you can do the same thing with filemanager-webpack-plugin like this without using tar-webpack-plugin.

    new FileManagerPlugin({
      events: {
        onEnd: {
          archive: [
            {
              source: '.wp_federation',
              destination: 'path/to/types.tar.gz',
              format: 'tar',
            }
          ],
        },
      },
    }),
simonepizzamiglio commented 2 years ago

Depending on your use case/setup you could solve this by having a tsconfig.json in the project root and leverage path-mapping to resolve the apps entry points (Remote Name + Exposed module):

/tsconfig.json:

...
"paths": {
      "app1/Remote": ["./packages/app1/src/app"],
      "app2/Remote": ["./packages/app2/src/app"]
}
...

/packages/app1/tsconfig.json & /packages/app2/tsconfig.json:

{
  "extends": "../../tsconfig.json"
}

I've setup a little prototype using this setup: rangle/federated-modules-typescript

This is the easier and quickest solution that worked for me! Thanks @micmro 🙏🏻

jrandeniya commented 2 years ago

If anyone is looking for a poly-repo solution, I've put together this sample app with some instructions to share the type definitions of exposed files: https://github.com/jrandeniya/federated-types-sample

It's relatively straight forward using the great work done by @touk/federated-types and webpack-remote-types-plugin

stefan-toubia commented 2 years ago

I've also been using the tsconfig.json paths solution and it has been working well, but it's not without issues. That is, those remotes that you've configured with paths will still need to be fully installed when you check types, or you'll end up with errors:

./packages/app1/src/app/useSelectedEntity.ts:2:21 - error TS2307: Cannot find module 'lodash/isEqual' or its corresponding type declarations.

2 import isEqual from "lodash/isEqual";
                      ~~~~~~~~~~~~~~~~
ScriptedAlchemy commented 2 years ago

We have released a Federated Types Plugin beta.

Theres some gaps in the type def bundling, we are aware of it and working on improvements.

https://app.privjs.com/buy/packageDetail?pkg=@module-federation/typescript

malopgrics commented 2 years ago

We have released a Federated Types Plugin beta.

Theres some gaps in the type def bundling, we are aware of it and working on improvements.

https://app.privjs.com/buy/packageDetail?pkg=@module-federation/typescript

It looks promising. Is there a plan to open source it in regular npm ?

ScriptedAlchemy commented 2 years ago

We have released a Federated Types Plugin beta.

Theres some gaps in the type def bundling, we are aware of it and working on improvements.

https://app.privjs.com/buy/packageDetail?pkg=@module-federation/typescript

It looks promising. Is there a plan to open source it in regular npm ?

Not currently, I generally only publish OSS to npm. Closed source, even if it's free, usually stays on priv

Perhaps in the future depending on data & feedback during beta & RC phases

steven-pribilinskiy commented 2 years ago

Hey @ScriptedAlchemy, I'm glad there's finally a solution emerging to share Typescript definitions among microfrontends from the author, despite of saying that I'm not a TS user so I'm no help here.!

I was trying other solutions like @touk/federated-types that reads a federation.config.json and generates types in node_modules@types__federated_types

It does that for local modules but does not collect types for modules that are exposed from node_modules.

Our federation.config.json config looks like this:

{
  "name": "commonLibs",
  "exposes": {
    "./AxiosHttp": "./src/api-services/axios-client/index",
    ...
    "./@cloudbeds/ui-library": "@cloudbeds/ui-library",
    "./react": "react",
    "./react-dom": "react-dom",
    "./react-query": "react-query",
    "./react-router-dom": "react-router-dom"
  }
}

Everything after ... is from node_modules and we want that to be part of commonLibs microfrontend so that will consume a single version of these without even installing react in consuming app's package.json, e.g.

import react from 'commonLibs/react';

Coupled with webpack-remote-types-plugin we could have updated types without uploading them to S3 or publishing to NPM. Unfortunately that doesn’t work with Dynamic remotes - it needs an actual URL from which the remote is served from. We use external-remotes-plugin and uses a dynamic variable name, e.g. [window.commonLibs] instead of localhost:4242

Here's a screen of the error we are experiencing:

image

steven-pribilinskiy commented 2 years ago

Now we are trying to achieve that with Federated Types Plugin beta.

It would be really great if the closed source plugin would have an official place to open issues. Probably this thread is currently serves this purpose

steven-pribilinskiy commented 2 years ago

@module-federation/typescript creates types in dist/@mf-typescript

The __types_index.json file contains list of type definition files for all exposed modules, here it's:

[
  "index.d.ts",
  "index.d.ts",
  "index.d.ts",
  "index.d.ts",
  "ui-library.d.ts",
  "react.d.ts",
  "react-dom.d.ts",
  "react-query.d.ts",
  "react-router-dom.d.ts"
]

Although it lists packages in node_modules, e.g. react.d.ts and ui-library.d.ts they are not included in dist/@mf-typescript

Also it would be great if that file's purpose would be documented here

steven-pribilinskiy commented 2 years ago

Compared to @touk/federated-types, instead of a single file with all types, @module-federation/typescript recreates the directory structure which is neat.

There's one serious issue with @module-federation/typescript plugin is that the types cannot be used for microfrontends that are served from production environment (unless we don't know something). E.g. imagine there are 10 federated apps

  1. shell - served with webpack-dev-server from localhost:4242
  2. commonLibs - from localhost:4243
  3. wmf3 - from https://production.com/wmf3
  4. wmf4 - from https://production.com/wmf4
  5. ...

Some are served with webpack-dev-server, others from production (e.g. CDN, AWS Cloudfront, etc.). For these we would like to publish an npm package with types.

It doesn't look like @module-federation/typescript has any configuration.

@ScriptedAlchemy are there any hidden options that we could provide to generate types that are usable when published as an NPM package and includes types for modules that expose node_modules? If not, would it be possible to extend the plugin to accommodate for those use-cases?

steven-pribilinskiy commented 2 years ago

Also it suffers from the issue with dynamic imports image

Even though here are examples in advanced-api and the dynamic System Host. So it's not something unusual to do.

We use the module-federation/external-remotes-plugin to achieve this

Also the Practical Module Federation has an example of Dynamically Loading Federated Modules for Webpack 4 and 5.

@ScriptedAlchemy how we can use the plugin in such environment?

Looks like module-federation/external-remotes-plugin and @module-federation/typescript are currently incompatible

steven-pribilinskiy commented 2 years ago

I've changed the URL to be localhost in remotes section and now we're getting this 404 error for exposed npm package

\node_modules\got\index.js:482
     proxy.emit('error', new got.HTTPError(statusCode, res.statusMessage, res.headers, opts), null, res);
    ^
GotError [HTTPError]: Response code 404 (Not Found)

  path: '/@mf-typescript/ui-library.d.ts',
  protocol: 'http:',
  url: 'http://localhost:9082/@mf-typescript/ui-library.d.ts',
  statusCode: 404,
  statusMessage: 'Not Found',

image

steven-pribilinskiy commented 2 years ago

Sorry for the numerous comments, I wish it can be hosted on Github, with Issues/Discussion tabs It can be an empty repo with a README only.

MR-BH commented 2 years ago

We have released a Federated Types Plugin beta.

Theres some gaps in the type def bundling, we are aware of it and working on improvements.

https://app.privjs.com/buy/packageDetail?pkg=@module-federation/typescript

I want to contribute to the repo, is there any way I can join the work? Wait for your reply. @ScriptedAlchemy

anthonycaron commented 2 years ago

I don't know if this was already mentioned in this thread but there are some nice solutions exposed in this blog post: https://spin.atomicobject.com/2022/07/19/typescript-federated-modules/

steven-pribilinskiy commented 2 years ago

For anyone interested, at Cloudbeds, we've released a public NPM package that was built on top of existing solutions

https://www.npmjs.com/package/@cloudbeds/webpack-module-federation-types-plugin

It's also planned to open source it on Github

gauravmakkar commented 2 years ago

@ScriptedAlchemy Any plan to add typescript support in federated modules?

ScriptedAlchemy commented 2 years ago

Already done. App.privjs.com search for module-federation/typescript

We did this months ago

ScriptedAlchemy commented 2 years ago

https://www.npmjs.com/package/@module-federation/typescript

vmagalhaes commented 2 years ago

I'm searching for a solution with Angular using the new typescript package. I tried to use it but with no success. But great initiative @ScriptedAlchemy

steven-pribilinskiy commented 2 years ago

The @cloudbeds/webpack-module-federation-types-plugin is now available on Github. Feel free to use Issues or Discussions tabs.

And let me thank @ScriptedAlchemy for the awesome Module Federation plugin upon which a lot of projects nowadays started to rely on. I hope that some day there won't be a need for a 3rd party plugin to deal with types and there'll be a standard documented way built into Webpack.next

ScriptedAlchemy commented 2 years ago

If we can merge or open PRs to module-federation/typescript to improve the "official" plugin that would be great. We open-sourced it to try and get more input and help since i dont write TS I just know webpack very well haha

stefan-toubia commented 2 years ago

@ScriptedAlchemy can you link the github repo?

vadym-oliinyk commented 2 years ago

@ScriptedAlchemy can you link the github repo?

I suppose https://github.com/module-federation/typescript

ScriptedAlchemy commented 2 years ago

thats the one, PR's welcome

sudheerkumar-golla commented 1 year ago

Hey @ScriptedAlchemy, I'm glad there's finally a solution emerging to share Typescript definitions among microfrontends from the author, despite of saying that I'm not a TS user so I'm no help here.!

I was trying other solutions like @touk/federated-types that reads a federation.config.json and generates types in node_modules@types__federated_types

It does that for local modules but does not collect types for modules that are exposed from node_modules.

Our federation.config.json config looks like this:

{
  "name": "commonLibs",
  "exposes": {
    "./AxiosHttp": "./src/api-services/axios-client/index",
    ...
    "./@cloudbeds/ui-library": "@cloudbeds/ui-library",
    "./react": "react",
    "./react-dom": "react-dom",
    "./react-query": "react-query",
    "./react-router-dom": "react-router-dom"
  }
}

Everything after ... is from node_modules and we want that to be part of commonLibs microfrontend so that will consume a single version of these without even installing react in consuming app's package.json, e.g.

import react from 'commonLibs/react';

Coupled with webpack-remote-types-plugin we could have updated types without uploading them to S3 or publishing to NPM. Unfortunately that doesn’t work with Dynamic remotes - it needs an actual URL from which the remote is served from. We use external-remotes-plugin and uses a dynamic variable name, e.g. [window.commonLibs] instead of localhost:4242

Here's a screen of the error we are experiencing:

image

Hi @steven-prybylynskyi , Could you please suggest me the package you have used to build types for modules exposed from node_modules(eg: react).

ilteoood commented 1 year ago

Hey @ScriptedAlchemy, I'm glad there's finally a solution emerging to share Typescript definitions among microfrontends from the author, despite of saying that I'm not a TS user so I'm no help here.! I was trying other solutions like @touk/federated-types that reads a federation.config.json and generates types in node_modules@types__federated_types It does that for local modules but does not collect types for modules that are exposed from node_modules. Our federation.config.json config looks like this:

{
  "name": "commonLibs",
  "exposes": {
    "./AxiosHttp": "./src/api-services/axios-client/index",
    ...
    "./@cloudbeds/ui-library": "@cloudbeds/ui-library",
    "./react": "react",
    "./react-dom": "react-dom",
    "./react-query": "react-query",
    "./react-router-dom": "react-router-dom"
  }
}

Everything after ... is from node_modules and we want that to be part of commonLibs microfrontend so that will consume a single version of these without even installing react in consuming app's package.json, e.g.

import react from 'commonLibs/react';

Coupled with webpack-remote-types-plugin we could have updated types without uploading them to S3 or publishing to NPM. Unfortunately that doesn’t work with Dynamic remotes - it needs an actual URL from which the remote is served from. We use external-remotes-plugin and uses a dynamic variable name, e.g. [window.commonLibs] instead of localhost:4242 Here's a screen of the error we are experiencing: image

Hi @steven-prybylynskyi , Could you please suggest me the package you have used to build types for modules exposed from node_modules(eg: react).

https://github.com/module-federation/universe/tree/main/packages/native-federation-typescript

ScriptedAlchemy commented 1 year ago

Typescript support will be implemented as a built-in capability in the coming iteration of module federation. Colloquially known as "v1.5"

You will see this in https://modernjs.dev as well

ScriptedAlchemy commented 8 months ago

Type Hint Support Tracking: https://github.com/module-federation/universe/issues/1943