'package.json' is not under 'rootDir'

I'm trying to import package.json in my TypeScript application:

import packageJson from '../package.json';

My tsconfig.json contains the following:

{
  "compilerOptions": {
    "rootDir": "./src/"
    "outDir": "./dist/",
    "baseUrl": ".",
    "resolveJsonModule": true
  }
}

The problem is that when I compile this, I get

error TS6059: File '/path/to/package.json' is not under 'rootDir' '/path/to/app/src/'. 'rootDir' is expected to contain all source files.

I'm not sure I understand the issue, because both ./src/ and /.dist have the same parent .., so TypeScript could just leave alone the import '../package.json' and it would work from either rootDir or outDir.

Anyway, I've tried the following, with unsatisfactory results:

  • remove rootDir - compilation works, but the dist will contain dist/src, which I don't want
  • remove outDir - then src gets polluted with .js files (and .js.map if sourceMap was true)
  • add @ts-ignore - compilation stops the the file that imports ../package.json

What's the workaround for this limitation, to keep generated files in dist, and allow importing from the parent directory of rootDir?

We can set resolveJsonModule to false and declare a module for *.json inside typings.d.ts which will require JSON files as modules and it will generate files without any directory structure inside the dist directory.

Monorepo directory structure
monorepo\
├─ app\
│  ├─ src\
│  │  └─ index.ts
│  ├─ package.json
│  ├─ tsconfig.json
│  └─ typings.d.ts
└─ lib\
   └─ package.json
app/typings.d.ts
declare module "*.json";
app/src/index.ts
// Import from app/package.json
import appPackageJson from '../package.json';

// Import from lib/package.json
import libPackageJson from '../../lib/package.json';

export function run(): void {
  console.log(`App name "${appPackageJson.name}" with version ${appPackageJson.version}`);
  console.log(`Lib name "${libPackageJson.name}" with version ${libPackageJson.version}`);  
}

run();
package.json contents
app/package.json
{
  "name": "my-app",
  "version": "0.0.1",
  ...
{

lib/package.json
{
  "name": "my-lib",
  "version": "1.0.1",
  ...
}

Now if we compile the project using tsc, we'll get the following dist directory structure:

app\
└─ dist\
   ├─ index.d.ts
   └─ index.js

And if we run it using node ./dist, we'll get the output from both app and lib package.json information:

$ node ./dist
App name "my-app" with version 0.0.1
Lib name "my-lib" with version 1.0.1

You can find the project repository here: https://github.com/clytras/typescript-monorepo

It is not possible for now. Typescript compiler try to keep your directory structure.

For example, your project look like:

src/
  shared/
    index.ts
  index.ts
package.json
tsconfig.json

Your tsconfig.json contains:

{
  "compilerOptions": {
    "outDir": "./build",
    "module": "commonjs",
    "target": "es6",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "noImplicitAny": true,
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true
  },
  "include": [
    "src/**/*"
  ]
}

As you see, the file does not include rootDir property, but when you call tsc command to compile the project, the output will look like:

build/
  shared/
    index.js
  index.js

The output does not contain src folder, because in my code, I just import and use inside src folder, like:

src/index.ts

import someName from './shared';

then, build/index.js will look like:

...
const shared_1 = __importDefault(require("./shared"));
...

as you see - require("./shared"), this mean it working fine with build folder structure.

Your "issue" appeared when you import a "outside" module

import packageJson from '../package.json';

So, what happen with "back" action - '../'? If you hope your output structure will be:

build/
  package.json
  index.js

then, how do they work with const packageJson = __importDefault(require("../package.json"));. Then Typescript compiler try to keep project structure:

build/
  package.json
  src/
    index.js

With a monorepo project, I think you need to create declaration files for each library, end then use references setting in the tsconfig file. Ex:

  1. In the ./lib01 folder, the lib import ./lib02 in their code. Tsconfig file will be like:
{
  "compilerOptions": {
    "declarationDir": "dist",
    "rootDir": "src"
  },
  "include": ["src/**/*"],
  "references": [ // here
    {
      "path": "../lib02"
    }
  ]
}
  1. lib02's tsconfig.json
 {
   "compilerOptions": {
    "declarationDir": "dist",
    "rootDir": "src",
    "composite": true // importance.
  }
 }

It depends on how and when you're reading "package.json". You can read it as file with NodeJS "fs" module at runtime, or just type const package = require("package.json").

In 2nd case Typescript will search it in root dir at compile time (refer to Typescript module resolution documentation).

You also can use "rootDirs" property instead of "rootDir" to specify array of root folders.

Comments
  • Does // @ts-ignore help?
  • I’m having the same issue. Adding // @ts-ignore doesn’t help.
  • the given import statement doesn't jive with the given tsconfig. Produces error. See my suggested edit.
  • Did you tried to set baseUrl instead of rootDir?
  • @DanDascalescu let me know if I answered you question about monorepos.
  • Thank you for the excellent answer! The other one was slightly simpler for my use case so I awarded the bounty, but thanks for reminding me about the monorepo granular access solution with subtrees - accepted!
  • @DanDascalescu My pleasure. I need to figure this stuff out for myself anyway. You of course know your needs better than I, but as to "simpler"... consider the advantages that tsc --build offers that you'll miss out on. See the bottom of this answer (the top is much the same, though the tsconfigs are correct... I missed some details above which I'll fix for future readers).
  • This breaks missing import and unused import features in vscode for me: github.com/microsoft/TypeScript/issues/38605
  • @SephReed let's see what they come back with on the Issue report. Type checking on used imports work correctly?
  • upvoted. between mine and yours we have two legit solutions :)
  • Simplest answer, so I'll award it the bounty. Thanks! The only, very minor, issue I see is a warning for value in typings.d.ts: A default export can only be used in an ECMAScript-style module.
  • @DanDascalescu thank you for the bounty. Where do you get that warning, when you try to import the modules, or maybe when running using ts-node? It would be very helpful if you can PR an example that causing that warning to the repository so I can further investigate it.