Coder Social home page Coder Social logo

dts-loader's Introduction

dts-loader

Webpack loader to collect .d.ts files, the difference between this loader and ts-loader is dts-loader only emits .d.ts files and it's designed specifically for the purpose of sharing webpack module federation exposed types

Thus, dts-loader will not only emits .d.ts files, but also will emit the entry file for the exposed modules based on the configuration of webpack module federation plugin's exposes section.

Install

yarn add dts-loader

Setup

1. dts-loader config

{
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'dts-loader',
            options: {
              name: 'app', // The name configured in ModuleFederationPlugin
              exposes: { // The exposes configured in ModuleFederationPlugin
                './Counter': './src/modules/Counter/Counter.component.tsx',
              },
              typesOutputDir: '.wp_federation' // Optional, default is '.wp_federation'
            },
          },
        ],
      },
    ],
  },
}

With the above configuration, type definitions are emitted to .wp_federation/app/dts

And the entry file for ./Counter module is emitted to .wp_federation/app/Counter.d.ts

2. Use the generated types

Then you can copy the entire folder: .wp_federation/app and drop it to, for example: node_modules/@types

or you can remap the imports so that TypeScript knows where to resolve types for the remote modules

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "*": ["*", "types/*"] // Wherever you want to put
    }
  },
}

Now you can drop app folder to ./types

3. Share the generated types

For better development experience, you can also create a tarball for the types where you can find in .wp_federation, for example, give it a name: app-dts.tgz.

tar czvf ./.wp_federation/app-dts.tgz -C ./.wp_federation/ app

You can deploy it with your application's assets, and then the host application can download from remote and unzip it to typeRoots. This would make sure the typings are always up-to-date when working across different teams/applications.

4. WebpackRemoteTypesPlugin

You can use WebpackRemoteTypesPlugin to automate step #3, it will download the tarball from remote and unzip it to the specified folder.

new WebpackRemoteTypesPlugin({
  remotes: {
    app: 'app@http://localhost:9000/',
  },
  outputDir: 'types',
  remoteFileName: '[name]-dts.tgz' // default filename is [name]-dts.tgz where [name] is the remote name, for example, `app` with the above setup
}),

The plugin will download tarball from http://localhost:9000/app-dts.tgz and unzip the tarball to ./types folder

More advanced setup

Note: the above setup will emit type definitions for the whole application(all ts files that webpack traverse started from the entries). To avoid emit unnecessary files, it would be better to have a separate webpack config for the exposes:

{
  entry: {
    './Counter': './src/modules/Counter/Counter.component.tsx',
  }
}

dts-loader's People

Contributors

cjh804263197 avatar dependabot[bot] avatar hongkiulam avatar martinerko avatar mfsepplive avatar ruanyl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

dts-loader's Issues

Top level type declaration is not created

Hiya, first off - thanks for making this plugin! It's super helpful.

I spent the last 12 hours banging my head against the wall trying to figure out how to get this loader to work correctly. All of the types were being generated and I was successfully copying them over to my other services using webpack-remote-types-plugin and tar-webpack-plugin to generate the tgz archives. However, my TSServer could not find the type declarations.

It turns out, the top-level type declaration file that should be a sibling to the dts file in the generated types directory was not being generated. So I added some logging to the code to figure out what was going wrong. I realized that my exposes config looked like this:

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

while your logic expects explicit module paths like:

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

Easy enough to workaround, but Module Federation works with the implicit index paths. It would save a lot of future headaches to add some support for implicit paths out of the box (also I generally prefer using the implicit paths because it looks neater).

Include opt in tar functionality?

Just want to get some thoughts on whether you would want to include tar'ing as part of this loader, given it's suggested in the README. I'm personally not fan of having to write a post build script for all my remote apps to tar the output and copy it into my assets folder.
I imagine it could look something like

use: [
  {
    loader: "dts-loader",
    options: {
      name: nextFederationConfig.name, // The name configured in ModuleFederationPlugin
      exposes: nextFederationConfig.exposes,
      typesOutputDir: ".wp_federation", // Optional, default is '.wp_federation'
      tarOutputDir: "public", // and only tar if this exists, default to not tar
    },
  },
],

Project loads the component but the typings still can't be found

image
image

tsconfig.json

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "jsx": "react-jsx",
    "allowJs": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "sourceMap": true,
    "paths": {
      "*": [
        "*",
        "types/*"
      ]
    },
  },
  "include": [
    "**/*.ts",
    "**/*.tsx"
  ],
  "references": [
    {
      "path": "./tsconfig.app.json"
    },
    {
      "path": "./tsconfig.spec.json"
    }
  ]
}

Thank you so much for this library. I finally got the types from my remote app to correctly copy over from the generated tar file into the host app using your webpack-remote-types-plugin. However typescript is still throwing a compiling error about the declaration file not being found. What am I missing?

Some additional context: The import path is correct. I consoled out Test before the return in the function and it shows the react component object.

repo: https://github.com/andrew-rubiano1989/nx-module-federation-test

Loader does not support exposed dependencies from node_modules

We have a module that only exposes our dependencies (react, react-dom, dayjs etc.), it seems that the loader ignores them and does not generates the dts files.

Here is an example of the exposes we have:

exposes: {
    './react': 'react',
    './react-dom': 'react-dom',
}

And our tsconfig:

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "target": "esnext",
    "outDir": "./dist",
    "rootDir": ".",
    "baseUrl": ".",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "skipLibCheck": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noEmitHelpers": true,
    "importHelpers": true,
    "typeRoots": ["./node_modules/@types"],
    "types": ["../../types", "./node_modules/@types", "node"]
  },
  "ts-node": {
    "compilerOptions": {
      "target": "ES6",
      "module": "CommonJS"
    }
  }
}

Do we miss something?

Support for React.forwardRef

Hey.

The loader doesn't seem to be working when using React.forwardRef().

e.g. an input component using React.forwardRef()

// components/Input/Input.tsx
import React from "react";

interface IInputProps {
  id: string;
}

const Input = React.forwardRef(({ id }: IInputProps, ref) => {
  return <input ref={ref}/>
});
export default Input;

dts-loader creates the file:

// dts/components/Input/Input.dts
declare const Input: any;
export default Input;

If I don't use forwardRef:

// components/Input/Input.tsx
import React from "react";

interface IInputProps {
  id: string;
}

const Input = ({ id }: IInputProps) => {
  return <input />
};
export default Input;

dts-loader creates propper typing:

// dts/components/Input/Input.dts
import React from "react";
interface IInputProps {
    id: string;
};
declare const Input: ({ id }: IInputProps) => JSX.Element;
export default Button;

Any thoughts on how to solve this? I could simply pass through a ref another way, but this seems like a bug & React.forwardRef() is meant to be best practice. I imagine this will be a common occurrence for people using module federation for UI libraries.

export default support

I'm exposing a simple module federated button component that uses export default:

// Button.tsx
...
export default Button;

dts-loader creates the following file:

// Button.d.ts
export * from './dts/components/Button/Button';

As I understand it, this does not expose default exports, only named exports. So typescript is not picking up types on my remote Button.

One way of fixing might be to change dts-loader to create the following file instead:

// Button.d.ts
export * from './dts/components/Button/Button';
import defaultExport from './dts/components/Button/Button';
export default defaultExport;

This handles both named and default export cases. Thoughts?

Include opt in tar functionality?

Just want to get some thoughts on whether you would want to include tar'ing as part of this loader, given it's suggested in the README. I'm personally not fan of having to write a post build script for all my remote apps to tar the output and copy it into my assets folder.
I imagine it could look something like

use: [
  {
    loader: "dts-loader",
    options: {
      name: nextFederationConfig.name, // The name configured in ModuleFederationPlugin
      exposes: nextFederationConfig.exposes,
      typesOutputDir: ".wp_federation", // Optional, default is '.wp_federation'
      tarOutputDir: "public", // and only tar if this exists, default to not tar
    },
  },
],

Create types without dist folder

Hi, I really happy that i found this lib, its work for me, But i Have a question, i used from the example npm commands for creating folder .wp_federation and got together and folder dist, could you tell me please which command can help me to create just archive without dist and unpacked files in .wp_federation? Thanks a lot

Unexpected token } in JSON at position 226

Hey, thanks for this awesome plugin. Any reason why I'm getting this unexpected token?

ERROR in ./src/index.tsx
Module build failed (from ./node_modules/dts-loader/lib/index.js):
SyntaxError: ~/kaoto-ui/tsconfig.json: Unexpected token } in JSON at position 226

Only thing I've done is install and add it, still on step 1. :)

Added my webpack.config.js for the host app, and tsconfig.json here: https://gist.github.com/kahboom/f771a5674d6f419f61c9c4e9ceeb51e7

Example

Please, could you add an example? Because my MFE doesn't generate the tarball

Support for nested exposures

Hey, I would like to my MF exposes object to look like:

exposes: {
  "./components/Button": "./src/components/Button/Button.tsx",
  "./components/Input": "./src/components/Input/Input.tsx",
  "./utils/time": "./src/utils/time.tsx"
}

It doesn't work as expected, no files are created. When I run a build, I get the following message:

Skip lib\src\components\Button\Button.tsx
...
  1. It might be worth showing the error message within the catch statement so the user knows why something wasn't generated.
  2. The error I'm getting is: ENOENT: no such file or directory, open 'lib\dist\lib\components\Button.d.ts'. It looks to be caused by fs.writeFileSync because the \lib\components directory does not exist.

dts-loader/src/index.ts

Lines 117 to 130 in 621492b

for (const [key, value] of Object.entries(loaderOptions.exposes)) {
if (key && value) {
const entryPath = path.resolve(cwd, value)
if (entryPath === fileName) {
const moduleFilename = `${key}.d.ts`
const modulePath = path.resolve(
cwd,
`${loaderOptions.typesOutputDir}/${loaderOptions.name}`
)
fs.writeFileSync(
path.resolve(modulePath, moduleFilename),
`export * from './${path.relative(
path.relative(cwd, modulePath),
o.name.replace('.d.ts', '')

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.