Search code examples
typescriptnpmpackage.jsontsconfigmonorepo

How do I structure my NPM package to contain subfolders for types and functions?


I am trying to create a NPM package that can be used locally to share types and functions in the rest of my project. Let's call my project wordle. As you could probably tell by the mention of types, this is a TypeScript project. Ideally I want to be able to import types and functions in the following way:

import { Move } from "wordle-shared/types";
import { getValidWords } from "wordle-shared/utils"

Can someone explain how I would need to structure this NPM package to achieve the desired structure? I've seen people use @ in their package name (e.g. @wordle-shared), if this is required, that is fine.

So far I have the following folder structure

wordle-shared
├── src
│   ├── types
│   └── utils
├── package.json
├── tsconfig.json
├── .gitignore

Solution

  • To my knowledge, there needs to be a build step for TS to load your package correctly, because it will be looking for d.ts files.

    You could maybe use git submodules to inject the library code directly in your project instead of deploying it and installing it as a package. I never tried it, nor did I try workspaces, but I think it would be transparent to TS, then you would not have to build and the library would still be versioned.

    Here is one way to do it as a package with tsc:

    wordle-shared
    ├── src
    │   ├── types.ts
    │   └── utils.ts
    ├── dist
    │   ├── types.d.ts
    │   └── utils.js
    │   └── utils.d.ts
    ├── package.json
    ├── tsconfig.json
    ├── .gitignore
    
    // wordle-shared package.json
    {
        "type": "module",
        "version": "1.0.0",
        "exports": {
            "./utils": {
                "import": "./dist/utils.js",
                "types": "./dist/utils.d.ts"
            }, "./types": {
                "types": "./dist/types.d.ts"
            }
        }
    }
    
    // wordle-shared tsconfing.json
    {
        "compilerOptions": {
            "outDir": "./dist",
        },
        "include": ["src"],
    }
    
    // .gitignore
    node_modules
    dist
    

    Then you can add the shared library as a dependency

    // wordle package.json
    {
      "dependencies": {
        "wordle-shared": "^1.0.0",
      }
    }
    

    You will need this compiler option for TS to detect the types fields in the library package.json

    // wordle tsconfing.json
    {
        "compilerOptions": {
            "moduleResolution": "nodenext"
        },
    }