10

This question: "Cannot find module" error when using TypeScript 3 Project References was somewhat helpful.

So was this one: Project references in TypeScript 3 with separate `outDir`

But I am still having trouble getting this working efficiently with VSCode and React.

Project structure:

  • client/src
  • common
  • server/src

common tsconfig:

{
  "compilerOptions": {
    "declaration": true,
    "declarationMap": true,
    "rootDir": ".",
    "composite": true,
    "outDir": "build"
  },
  "references": []
}

client tsconfig:

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react",
  },
  "include": [
    "src",
  ],
  "references": [
    {
      "path": "../common"
    }
  ]
}

server tsconfig

{
    "compilerOptions": {
        "target": "es6",
        "lib": [
            "dom",
            "esnext"
        ],
        "module": "commonjs",
        "sourceMap": true,
        "outDir": "build",
        "allowJs": true,
        "moduleResolution": "node",
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "noImplicitReturns": true,
        "noUnusedLocals": true,
        "baseUrl": ".",
    },
    "include": [
        "src/**/*"
    ],
    "references": [
        {
            "path": "../common"
        }
    ]
}

I am using create-react-app in client. This has caused compilation issues. I believe I have solved this with create-app-rewired and customize-cra using this for config-overrides.js

const { override, removeModuleScopePlugin, getBabelLoader } = require("customize-cra");
const path = require("path");

const addCommon = (config) => {
  const loader = getBabelLoader(config, false);
  const commonPath = path.normalize(path.join(process.cwd(), "../common")).replace(/\\/g, "\\");
  loader.include = [loader.include, commonPath];
  return config;
};
module.exports = override(
  addCommon,
  removeModuleScopePlugin(),
);

In common, I have created a file index.ts which exports code from every file.

export * from "./messages/toServer/AuthMessage";

for example.

On the front-end and back-end, I can import code (to use) with a handwritten import statement:

import { AuthMessage } from "../../../common/build/messages/toServer/AuthMessage";

VSCode doesn't give any suggestions, but it accepts the import statement assuming the code is built.

I run tsc -b -w in common. This seems to work with manual imports.

It is possible to have:

  • IntelliSense / auto-import When writing types such as AuthMessage
  • Usable code that is imported. I have seen examples where types are supported, but code is not generated from common. Untested, but I imagine this works well for interfaces in a common directory
  • Usability with React. Some relevant jabber: https://github.com/facebook/create-react-app/issues/6799. ts-loader supports project references, but I am unsure the status of babel-loader, which to the best of my knowledge, is the loader used by CRA. I got a partial solution with the code I provided above.

Guidance here: https://www.typescriptlang.org/docs/handbook/project-references.html#guidance is very confusing. Any help with an explanation here is appreciated.

Other solutions?

  • I don't want to publish and update a private NPM module.
  • Using a program such as dsynchronize: http://dimio.altervista.org/eng/ might be easier where code is duplicated across two projects, but included in the src of both projects. It's not clean, and I can see some disasters with refactoring and an IDE attempting to re-negotiate import paths if the program is running...

Any help is appreciated.

2
  • Yes it is possible to have auto-import, VSCode's auto-import is terrible compared to webstorms which is god tier. I Would suggest swapping to Webstorm and i can almost gaurentee the auto import will pick it up. Is the code your sharing just types or is it values aswell? Commented Jan 1, 2020 at 21:01
  • how do you deal with the non-react output location? it is not added to react index.html Commented Mar 3, 2020 at 16:45

1 Answer 1

2

It appears to be working. The steps I took...

Create a base tsconfig in the project directory. (This is documented)

{
  "references": [
    {
      "path": "./common"
    }
  ],
  "files": []
}

Add "extends": "../tsconfig" to both sub project tsconfigs.

export everything from all the files in common/src. (using /common/index.ts) Update import paths to the common directory import { ClassName } from "../../common" (not individual build folders)

Run tsc -b in non-react projects for compilation. After very simple initial testing, it looks like my rewired react config just picks up changes to common. The config-overrides.js is a necessity.

This does appear to work in VSCode, however, after testing, I agree auto-import is superior in WebStorm.

When creating new files in common, they must be exported in /common/index.ts WebStorm auto import will give you a clue, VSCode is a little less obvious.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.