'package.json' is not under 'rootDir'

前端 未结 4 1057
醉梦人生
醉梦人生 2020-11-29 10:56

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

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

My tsconfi

相关标签:
4条回答
  • 2020-11-29 11:39

    This is possible, and it turns out, not hard.

    The reason the solution is not obvious is because typescript relies on the rootDir to decide the directory structure of the output (see this comment from Typescript's bossman), and only code included in the output or in package dependencies can be imported.

    • If you set rootDir to the root of your project, package.json gets emitted to the root of outDir and can be imported. But then your compiled src files get written to outDir/src.
    • If you set rootDir to src, files in there will compile to the root of outDir. But now the compiler won't have a place to emit package.json, so it issues "an error because the project appears to be misconfigured" (bossman's words).

    solution: use separate Typescript sub-projects

    A Typescript project is defined by a tsconfig file, is self-contained, and is effectively bounded by its rootDir. This is a very good thing, as it lines up with principles of encapsulation.

    You can have multiple projects (e.g. a main and a set of libs) each in their own directory and with their own tsconfig. Dependencies between them are declared in the tsconfig file using Typescript Project References.

    I admit, the term "projects" is a poor one, as intuitively it refers to the whole shebang, but "modules" and "packages" are already taken in this context. Think of them as "subprojects" and it will make more sense.

    We'll treat the src directory and the root directory containing package.json as separate projects. Each will have its own tsconfig file.

    1. Give the src dir its own project.

      ./src/tsconfig.json:

      {
        "compilerOptions": {
          "rootDir": ".",
          "outDir": "../dist/",
          "resolveJsonModule": true
        },
        "references": [      // this is how we declare a dependency from
          { "path": "../" }  // this project to the one at the root dir`
        ]
      }   
      
    2. Give the root dir its own project.

      ./tsconfig.json:

      {
        "compilerOptions": {
          "rootDir": ".",
          "outDir": ".",  // if out path for a file is same as its src path, nothing will be emitted
          "resolveJsonModule": true,
          "composite": true  // required on the dependency project for references to work
        },
        "files": [         // by whitelisting the files to include, TS won't automatically
          "package.json"   // include all source below root, which is the default.
        ]
      }
      
    3. run tsc --build src and voilà!

      This will build the src project. Because it declares a reference to the root project, it will build that one also, but only if it is out of date. Because the root tsconfig has the same dir as the outDir, tsc will simply do nothing to package.json , the one file it is configured to compile.

    this is great for monorepos

    • You can isolate modules/libraries/sub-projects by putting them in their own subdirectory and giving them their own tsconfig.

    • You can manage dependencies explicitly using Project References, as well as modularize the build:

      From the linked doc:

      • you can greatly improve build times

      A long-awaited feature is smart incremental builds for TypeScript projects. In 3.0 you can use the --buildflag with tsc. This is effectively a new entry point for tsc that behaves more like a build orchestrator than a simple compiler.

      Running tsc --build (tsc -b for short) will do the following:

      • Find all referenced projects
      • Detect if they are up-to-date
      • Build out-of-date projects in the correct order

      Don’t worry about ordering the files you pass on the commandline - tsc will re-order them if needed so that dependencies are always built first.

      • enforce logical separation between components

      • organize your code in new and better ways.

    It's also very easy:

    • src/tsconfig.json

      Even if you have no code at the root, this tsconfig can be where all the common settings go (the others will inherit from it), and it will enable a simple tsc --build src to build the whole project (and with --force to build it from scratch).

      {
        "compilerOptions": {
          "rootDir": ".",
          "outDir": "../build/",
          "resolveJsonModule": true,
          "composite": true
        },
        // this root project has no source of its own
        "files": [],
        // but building this project will build all of the following:
        "references": [
          { "path": "./common" }
          { "path": "./projectA" }
          // include all other sub-projects here  
        ]
      }
      
      • src/common/tsconfig.json

        Because common has no references, imports are limited to targets within its directory and npm_modules. You could even restrict the latter, I believe, by giving it its own package.json.

            {
             "compilerOptions": {
                "rootDir": ".",
                "outDir": "../../build/common",
                "resolveJsonModule": true,
                "composite": true
              }
            }
        
      • src/projectA/tsconfig.json

        projectA can import common because of the declared reference.

            {
              "compilerOptions": {
                "rootDir": ".",
                "outDir": "../../build/libA",
                "resolveJsonModule": true,
                "composite": true
              },
              "references": [
                { "path": "../common" }
              ]
            }
        
    0 讨论(0)
  • 2020-11-29 11:40

    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

    0 讨论(0)
  • 2020-11-29 11:40

    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.
      }
     }
    
    0 讨论(0)
  • 2020-11-29 11:46

    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.

    0 讨论(0)
提交回复
热议问题