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

How typesafe can a remote be with Typescript? #20

Closed
echarles opened this issue May 4, 2020 · 86 comments
Closed

How typesafe can a remote be with Typescript? #20

echarles opened this issue May 4, 2020 · 86 comments
Assignees
Labels
question Further information is requested

Comments

@echarles
Copy link

echarles commented May 4, 2020

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.

declare module "app2/Button" {
const Button: React.ComponentType;
export default Button;
}

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?

@echarles
Copy link
Author

echarles commented May 4, 2020

@ScriptedAlchemy

@saulshanabrook
Copy link

saulshanabrook commented May 5, 2020

I believe, in our case, we should be able to be fine with not having type checking for remote libraries. We can use shared libraries for extension authors import core libraries, so they don't need to be remote and dynamically imported.

@echarles
Copy link
Author

echarles commented May 5, 2020

I have tried a hacky way to benefit from type-safety importing the definition of the remote project (app2, here widgets) in the host project (app1). As shown on the below image, lazy returns the correct type and non acceptable props are underlined in red. I guess a solution would be to publish the types separately and a 3rd party could depend on those types.

Screenshot 2020-05-05 at 17 51 48

@echarles echarles changed the title How typesafe can a remote be? How typesafe can a remote be with Typescript? May 5, 2020
@ScriptedAlchemy
Copy link
Member

Or have a monorepo of types

@maraisr
Copy link
Member

maraisr commented May 8, 2020

What I have done is use node to generate a .d.ts file, that are loaded by our global.d.ts file.

So each Remote outputs a .d.ts file that go declare module "dlaWidgets/Widets" {}, with using the ts.createProgram you can tap into the typeChecker stuff that i use to print its public api. Kinda like getting tsc to ouput definition files, but instead spit them into a single file.

Then each of my hosts import this .d.ts file, which I have registered in a global.d.ts, that all of my projectReferenced monorepo apps include.

So there is a small disconnect between a webpack config, and the types. But any exposed things, are automagically available with types in my entire app. So ts might say its a valid import, but just need to actially configure it with webpack also.

Working on open sourcing this generative thing - just gotta make sure it works in all use cases.

@ScriptedAlchemy ScriptedAlchemy pinned this issue May 12, 2020
@echarles
Copy link
Author

@maraisr your approach sounds very interesting. Keep us posted if you open source it.

@maraisr maraisr self-assigned this May 14, 2020
@ScriptedAlchemy ScriptedAlchemy added the question Further information is requested label May 20, 2020
@ScriptedAlchemy
Copy link
Member

ill raise this with Microsoft - they will have to support federation since they are going to be using it heavily

@echarles
Copy link
Author

ill raise this with Microsoft - they will have to support federation since they are going to be using it heavily

Awesome!

@zhangwilling
Copy link
Contributor

zhangwilling commented May 21, 2020

mono repo is not recomended when our app is very large,independent repo is better.

💡We can watch and output independent repo's d.ts, then start a server which can make d.ts could be downloaded. When other apps need d.ts,just download it to your project from local server.

@ckken
Copy link

ckken commented May 31, 2020

same issue and suggest like deno remote url type.d.ts

@ScriptedAlchemy
Copy link
Member

I found some devs solving this by modifying TS loader. Specifically for module Federation

@ckken
Copy link

ckken commented Jun 10, 2020

how to fix it

@zhangwilling
Copy link
Contributor

i think it should be solved by IDE, such as vscode.

@ScriptedAlchemy
Copy link
Member

yeah IDE can solve it, assuming it supports that. Meeting with Microsoft in august - seeking changes to TS to support MF, in doing so IDEs will have to follow the spec

@ScriptedAlchemy
Copy link
Member

This remains a solution

What I have done is use node to generate a .d.ts file, that are loaded by our global.d.ts file.

So each Remote outputs a .d.ts file that go declare module "dlaWidgets/Widets" {}, with using the ts.createProgram you can tap into the typeChecker stuff that i use to print its public api. Kinda like getting tsc to ouput definition files, but instead spit them into a single file.

Then each of my hosts import this .d.ts file, which I have registered in a global.d.ts, that all of my projectReferenced monorepo apps include.

So there is a small disconnect between a webpack config, and the types. But any exposed things, are automagically available with types in my entire app. So ts might say its a valid import, but just need to actially configure it with webpack also.

Working on open sourcing this generative thing - just gotta make sure it works in all use cases.

@glebmachine
Copy link

@zhangwilling it's not a part of IDE, it's all about typechenking of typescript compiler itself

@maraisr
Copy link
Member

maraisr commented Jun 14, 2020

I like the IDE idea @zhangwilling, but that will be more about aiding the development flow. However... I think this should still remain in the compiler itself, or a definition file that the compiler reads in. Personally feel its the latter.

If you solve to get the compiler to be aware of the remotes, then you don't have to worry about IDE, and specific IDE support as all IDE's will get this knowledge for free.

My solution outlined above still holds true. Its not terribly bad to implement. I'm hoping to get something open sourced soon.

Just wanting to get an idea from the community:

  1. Are you using a monorepo?
  2. Are your exposes objects exposed as standard javascript/typescript files? (Rather than inline in your webpack.config.js)
  3. Are the request's standard node-esk requests, ie no @svgr/webpack!./icon.svg, where the request would firsts require a loader?

I've taken some assumptions to how I build software, but just wanting to get more scope so that this tool when released would solve issues for the majority.

@zhangwilling
Copy link
Contributor

zhangwilling commented Jun 15, 2020

@zhangwilling it's not a part of IDE, it's all about typechenking of typescript compiler itself

@glebmachine yes, it is not part of IDE. Maybe TS can provide extra typing file config, but IDE plugin can do this, it could be improved much by IDE. Same as maven in IDEA, maven is not part of IDEA, but it does. Because it's a very important infrastrure.👻

@zhangwilling
Copy link
Contributor

I like the IDE idea @zhangwilling, but that will be more about aiding the development flow. However... I think this should still remain in the compiler itself, or a definition file that the compiler reads in. Personally feel its the latter.

If you solve to get the compiler to be aware of the remotes, then you don't have to worry about IDE, and specific IDE support as all IDE's will get this knowledge for free.

My solution outlined above still holds true. Its not terribly bad to implement. I'm hoping to get something open sourced soon.

Just wanting to get an idea from the community:

  1. Are you using a monorepo?
  2. Are your exposes objects exposed as standard javascript/typescript files? (Rather than inline in your webpack.config.js)
  3. Are the request's standard node-esk requests, ie no @svgr/webpack!./icon.svg, where the request would firsts require a loader?

I've taken some assumptions to how I build software, but just wanting to get more scope so that this tool when released would solve issues for the majority.

@maraisr
monorepo is not recommend when facing large project. so a large project would be seprated to many litttle repos by domain or module. But, we usually would put them together into same parent directory or we use worksapce in VSCode to oragnize them together logically 😈.

@glebmachine
Copy link

@zhangwilling yeah

But, typescript are trying to resolve imports. And fails when facing federation module import declaration. It looks like we have to be able to declare special federation modules right into tsconfig.json file or similar way.

For now, i'm workaround this with by loads file by document.createElement('script').

@micmro
Copy link

micmro commented Jun 16, 2020

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: https://github.com/rangle/federated-modules-typescript

@maraisr
Copy link
Member

maraisr commented Jun 17, 2020

You got to be careful with that approach though. It does mean that you cannot also use path mappings for other things in your app, because if you are you're probably also using tsconfig paths webpack resolver, in which case module federation plugin can't "remote" those. You can but you'd have to have a special build-time tsconfig that excludes the MF mappings.

@micmro
Copy link

micmro commented Jun 17, 2020

Thanks @maraisr , that is a very good point I had not considered.
I suppose then it is a trade-off of manual remote interface vs potential manual MF path-mapping overwrites.

@doerme
Copy link

doerme commented Jun 29, 2020

1.webpack provide a template for the "d.ts" module building
2.webpack config decare the use of remote "d.ts" module
3.IDE support using static inspection for the second point

@ScriptedAlchemy
Copy link
Member

ScriptedAlchemy commented May 9, 2022

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
Copy link

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
Copy link
Member

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
Copy link

steven-pribilinskiy commented Jun 12, 2022

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
Copy link

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
Copy link

@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
Copy link

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
Copy link

steven-pribilinskiy commented Jun 12, 2022

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
Copy link

steven-pribilinskiy commented Jun 12, 2022

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
Copy link

steven-pribilinskiy commented Jun 12, 2022

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
Copy link

MR-BH commented Jul 8, 2022

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
Copy link

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
Copy link

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
Copy link

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

@ScriptedAlchemy
Copy link
Member

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

We did this months ago

@ScriptedAlchemy
Copy link
Member

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

@vmagalhaes
Copy link

vmagalhaes commented Aug 29, 2022

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
Copy link

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
Copy link
Member

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
Copy link

@ScriptedAlchemy can you link the github repo?

@vadym-oliinyk
Copy link

@ScriptedAlchemy can you link the github repo?

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

@ScriptedAlchemy
Copy link
Member

thats the one, PR's welcome

@sudheerkumar-golla
Copy link

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
Copy link
Collaborator

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
Copy link
Member

ScriptedAlchemy commented Aug 26, 2023

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
Copy link
Member

Type Hint Support Tracking: module-federation/core#1943

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests