Sharing code between projects using TypeScript and webpack
Asked Answered
E

2

17

I want to share code between two TypeScript projects. I don't want to publish shared code to NPM-- just want to put shared code in one project and use it in another project. I'm using Webpack and awesome-ts-loader. Current folder structure (simplified) is like this:

/devroot/
  mainProject/
    tsconfig.json
    src/
      shared/
        SomeSharedTypes.ts
  apiProject/
    tsconfig.json
    webpack.config.js
    src/
      UseSomeSharedType.ts

In UseSomeSharedType.ts, I want to be able to import types from SomeSharedTypes.ts.

I tried an obvious solution like this:

import {SharedType} from '../../mainProject/src/shared/SomeSharedTypes'

But the TS compiler gave me this error:

TS6059: File '/devroot/mainProject/src/shared/SomeSharedTypes.ts' is not under 'rootDir' '/devroot/apiProject'. 'rootDir' is expected to contain all source files.

Einhorn answered 24/9, 2018 at 20:46 Comment(0)
E
17

The first idea I got from this Medium article, which was to use TypeScript's non-relative module imports feature. This would allow me to write my imports like this:

import {SharedType} from '@foo/SomeSharedTypes'

Using the techniques described in the article, I added a paths configuration to my tsconfig.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@foo/*": ["../mainProject/src/shared/*"],
    },
    "rootDir": "./",
    ...
  }
}

Then, again as the article recommends, for users of awesome-typescript-loader, I had to modify my webpack.config.js to add a resolution plugin:

const { TsConfigPathsPlugin } = require('awesome-typescript-loader');
. . .
  resolve: {
    extensions: ['.js', '.json', '.ts'],
    plugins: [
      new TsConfigPathsPlugin(),
    ],
  }

Important: this plugin needs to go into the resolve/plugins section of the file, not the root-level "plugins" section! If you put the resolver plugin in the wrong place, you'll get this error:

resolver.ensureHook is not a function

The steps above got me further along in the process-- TypeScript was now able to find my files!--but I still got the same error later in webpack's execution: 'rootDir' is expected to contain all source files.

After a lot more Googling, I found a solution in this StackOverflow answer: instead of a single rootDir configuration, TS has a rootDirs setting that allows multiple roots. I removed my rootDir setting from tsconfig.json and added a rootDirs setting:

"rootDirs": [
  "./",
  "../mainProject",
],

Next, I ran into a webpack error on the other-project TypeScript file I was including:

Module parse failed: Unexpected token (3:15)

You may need an appropriate loader to handle this file type.

After another hour of troubleshooting, I figured out that I need to tell the webpack loader about my new shared folder. Like this:

const path = require('path');
. . .
  rules: [
    {
      test: /\.[jt]sx?$/,
      loader: "awesome-typescript-loader",
      include: [
        __dirname,
        path.resolve(__dirname, "../mainProject/src/shared/")
      ],
      exclude: /node_modules/
    },

That worked! The nice part about this solution is that I can refactor my folder structure without changing source code. All I'd need to change is tsconfig.json and webpack.config.js.

I'm admittedly new to using webpack and TypeScript, so there may be a better solution than the one above... but the one above worked for me!

Sharing the solution here to make it easier for the next developer to find it.

Einhorn answered 24/9, 2018 at 20:46 Comment(2)
After I added "baseUrl"/"paths" to tsconfig.json and loader: 'awesome-typescript-loader' and ``` plugins: [ new TsConfigPathsPlugin() ] ``` it seems to be enough, rootDirs and include not necessary. At least both tsc and webpack succeed.Delacroix
Just wanted to chime in and thank you for pointing out "rootDirs" - that solved a lot of my headaches, as I wasn't aware that was an option!Rase
M
0

I would strongly recommend using the Nx workspace management tool as it is specifically build to solve these kind of setup issues. You can simply create a library within your repository that can then be imported everywhere (see: https://nx.dev/angular/tutorial/08-create-libs). Me and my team are heavily relying on Nx now for over 6 months and could not imagine managing a TypeScript repository without it, give it a try! https://nx.dev

If using Nx is not an option for you, you can model the way the implemented code-sharing:

  1. Create a new folder
  2. Add an index.ts file which exports everything that should be accessible from outside the library
  3. Add an entry inside your tsconfig.json -> compilerOptions -> paths that points to the index.ts of your library
  4. Import the shared code via the import name you specified inside the tsconfig.json
Mohenjodaro answered 17/11, 2019 at 18:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.