Webpack warns "module not found" on Typescript declaration

260 Views Asked by At

I'm trying to create an isomorphic Typescript library that exports a function--let's call it myFunc--that has different logic and uses different internal types depending on whether it is compiled for node or browser. The function signature exported by this library, however, is exactly the same, i.e., it exposes the same API to the consumer.

My strategy for doing this is to write separate functions in in src/browser/index.ts and src/node/index.ts for browser and node, respectively, but put the function signature in a src/common/types.ts file as a declaration. I then set the tsconfig.json for src/browser and src/node to only compile, and not emit any declarations. And, conversely, the tsconfig.json for common/types.ts to emit declarations but not compile. I then use webpack to create two different .js files--browser.js and node.js, and tsc to emit the declarations as type.d.ts. (Webpack probably isn't strictly necessary for this part of this example, but my real-world example is a little more complex.)

In short, my package exports three files -- types.d.ts, browser.js, node.js--reflecting the common types and the two different .js version. And I have a package.json file that defines types, browser, and main to point any consumers to each of these files.

So far so good. Now I go to import this package into an app--let's say a browser app. Everything seems to work fine. Typescript sees the types and let's me import them, etc. Webpack will also bundle the app and pick the correct .js file (in this case, the browser file).

But I get the following warning in webpack on build:

export 'myFunc' (imported as 'myFunc') was not found in 'my-lib' (module has no exports)

This is clearly not right. The IDE has no problem finding the exports using Typescript. The .js files do have exports. And the .js files actually get correctly imported into the final bundle. (This is only a warning; it doesn't fail the build.)

But I can't figure out what is causing this error or how to eliminate it (other than just suppressing it).

I'm not sure if the problem is webpack, ts-loader (which I'm using to compile TS), or something about my underlying TS setup in the underlying library.

I've posted a complete, working, minimal example of this (using a rush repository to handle the package and the app together) on github. Here it is. All you have to do to see the error is clone the repo and then:

npm install @microsoft/rush
rush install
rush update
cd myLib
rushx build
cd ../myApp
rushx build
1

There are 1 best solutions below

0
sam256 On

It turns out the issue here was not setting webpack to correctly export as library type "module". This revised webpack config does the trick.


const path = require("path")

const commonConfig = {
  stats: {
    errorDetails: true
  },
  mode: "development",
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: {
          loader: "ts-loader"
        }
      }
    ]
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
    extensionAlias: {
      ".js": [".ts", ".js"],
      ".jsx": [".tsx", ".jsx"],
      ".mjs": [".mts", ".mjs"]
    }
  }
}

const browserConfig = {
  ...commonConfig,
  experiments: {
    outputModule: true
  },
  entry: { browser: "./src/browser/index.ts" },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: {
          loader: "ts-loader"
        }
      }
    ]
  },
  output: {
    filename: "browser.js",
    path: path.resolve(__dirname) + "/lib",
    library: {
      type: "module"
    },
    globalObject: "this"
  }
}

const nodeConfig = {
  ...commonConfig,
  experiments: {
    outputModule: true
  },
  entry: {
    node: "./src/node/index.ts"
  },
  target: "node",
  output: {
    filename: "node.js",
    path: path.resolve(__dirname) + "/lib",
    chunkLoading: "import",
    chunkFormat: "module",
    library: {
      type: "module"
    },
    globalObject: "this"
  }
}
module.exports = [browserConfig, nodeConfig]