javascript - 'package.json' er ikke under 'rootDir'


Jeg prøver å importere package.json i min TypeScript-applikasjon:

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

Min tsconfig.json inneholder følgende:

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

Problemet er at når jeg kompilere dette, får jeg

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

Jeg er ikke sikker på om jeg forstår problemet, fordi både ./src/ og ./dist/ har samme overordnede .., så TypeScript kunne bare la import '../package.json' være, og det ville fungere fra enten rootDir eller outDir.

Uansett, jeg har prøvd følgende, med utilfredsstillende resultater:

  • fjern rootDir - kompileringen fungerer, men dist vil inneholde dist/src, noe jeg ikke vil
  • fjern outDir - da blir src forurenset med .js-filer (og .js.map hvis sourceMap var sann)
  • legg til @ts-ignore - kompileringen stopper filen som importerer ../package.json

Hva er løsningen på denne begrensningen, for å beholde genererte filer i dist, og tillate import fra overordnet mappe til rootDir?

Svar #1

Dette er mulig, og det viser seg å være ikke vanskelig.

Grunnen til at løsningen ikke er åpenbar er fordi Typescript er avhengig av rootDir for å bestemme katalogstrukturen til outputen (se denne kommentaren fra Typescripts sjef), og kun kode som er inkludert i outputen eller i pakkens avhengigheter kan importeres.

  • Hvis du setter rootDir til roten av prosjektet ditt, blir package.json sendt til roten av outDir og kan importeres. Men da blir dine kompilerte src-filer skrevet til outDir/src.
  • Hvis du setter rootDir til src, vil filer der inne kompileres til roten av outDir. Men nå vil ikke kompilatoren ha et sted å avgi package.json, så den gir "en feil fordi prosjektet ser ut til å være feilkonfigurert" (bossman's ord).

løsning: bruk separate Typescript-underprosjekter

Hvert Typescript-prosjekt er selvstendig, definert av sin egen tsconfig, med sin egen rootDir. Dette samsvarer med prinsippet om innkapsling.

Du kan ha flere prosjekter (f.eks. en hoved og et sett med biblioteker) hver i sin egen rootDir og med sin egen tsconfig. Avhengigheter mellom dem erklæres i den avhengiges tsconfig ved bruk av Typescript Project References.

Jeg innrømmer at begrepet "prosjekter" er et dårlig valg, da det intuitivt refererer til hele greia, men "moduler" og "pakker" er allerede tatt. Tenk på dem som underprosjekter så vil det gi mer mening.

For å løse ditt spesifikke problem, vil vi behandle src-mappen og rotmappen som inneholder package.json som separate prosjekter. Hvert av dem vil ha sin egen tsconfig.

  1. Gi src-mappen sitt eget prosjekt.

    ./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. Gi roten sin egen prosjekt.

    ./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, you avoid the default TS behavior, which
        "package.json"   // will include everything, resulting in `src` being included in both projects (bad)
      ]
    }
    
  3. kjør tsc --build src og voilà!

    Dette vil bygge src-prosjektet. Fordi det erklærer en referanse til rotprosjektet, vil det også bygge det, men kun hvis det er utdatert. Fordi rot-tsconfigen har samme katalog som outDir, vil tsc rett og slett ikke gjøre noe med package.json, den ene filen det er konfigurert til å kompilere.

dette er flott for monorepoer

  • Du kan isolere moduler/biblioteker/underprosjekter ved å plassere dem i sin egen undermappe og gi dem sin egen tsconfig.

  • Du kan håndtere avhengigheter eksplisitt ved å bruke prosjektreferanser, samt modulere byggeprosessen:

    Fra det lenkede dokumentet:

    • du kan forbedre byggetider betydelig

      En etterlengtet funksjon er smarte inkrementelle bygg for TypeScript-prosjekter. I 3.0 kan du bruke --build-flagget med tsc. Dette er effektivt et nytt inngangspunkt for tsc som oppfører seg mer som en byggeorkestrator enn en enkel kompilator.

      Å kjøre tsc --build (tsc -b for kort) vil gjøre følgende:

      • Finn alle refererte prosjekter
      • du kan forbedre byggetidene betydelig
        En etterlengtet funksjon er smarte inkrementelle bygg for TypeScript-prosjekter. I 3.0 kan du bruke --build-flagget med tsc. Dette er effektivt et nytt inngangspunkt for tsc som oppfører seg mer som en byggekoordinator enn en enkel kompilator. Å kjøre tsc --build (tsc -b for kort) vil gjøre følgende:
      • Finn alle refererte prosjekter

      • Finn ut om de er oppdaterte
      • Bygg utdaterte prosjekter i korrekt rekkefølge

      Ikke bekymre deg for rekkefølgen på filene du sender på kommandolinjen - tsc vil omorganisere dem om nødvendig slik at avhengigheter alltid bygges først.

    • håndheve logisk separasjon mellom komponenter

    • organiser koden din på nye og bedre måter.

Det er også veldig enkelt:

  1. En rot tsconfig for delte alternativer og for å bygge alle underprosjekter med en enkel tsc --build-kommando (med --force for å bygge dem fra bunnen av)

    src/tsconfig.json

    {
      "compilerOptions": {
        "outDir": ".", // prevents this tsconfig from compiling any files
    
        // we want subprojects to inherit these options:
        "target": "ES2019", 
        "module": "es2020", 
        "strict": true,
        ...
      },
    
      // configure this project to build all of the following:
      "references": [
        { "path": "./common" }
        { "path": "./projectA" }
      ]
    }
    
  2. Et "vanlig" bibliotek som blir hindret i å importere fra de andre delprosjektene fordi det ikke har noen prosjektreferanser

    src/common/tsconfig.json

    {
      "extends": "../tsconfig.json", //inherit from root tsconfig
    
      // but override these:
      "compilerOptions": {
        "rootDir": ".",
        "outDir": "../../build/common",
        "resolveJsonModule": true,
        "composite": true
      }
    }
    
    
  3. Et delprosjekt som kan importere common på grunn av den erklærte referansen. src/projectA/tsconfig.json

    {
      "extends": "../tsconfig.json", //inherit from root tsconfig
    
      // but override these:
      "compilerOptions": {
        "rootDir": ".",
        "outDir": "../../build/libA",
        "resolveJsonModule": true,
        "composite": true
      },
      "references": [
        { "path": "../common" }
      ]
    }
    
    

Svar #2

Vi kan sette resolveJsonModule til false og erklære en modul for *.json inne i typings.d.ts, som vil kreve JSON-filer som moduler, og det vil generere filer uten noen mappestruktur inne i dist-katalogen.

Monorepo-katalogstruktur

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();

app/package.json-innhold

{
  "name": "my-app",
  "version": "0.0.1",
  ...
}

lib/package.json-innhold

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

Nå, hvis vi kompilerer prosjektet ved å bruke tsc, vil vi få følgende dist-katalogstruktur:

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

Og hvis vi kjører det med node ./dist, vil vi få utdataene fra både app og lib package.json-informasjonen:

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

Du kan finne prosjektarkivet her: https://github.com/clytras/typescript-monorepo

Svar #3

Det finnes en ryddig tretrinns løsning med Node 16+ LTS og TypeScript 4.7+ for pakker som bruker ES-moduler i stedet for CommonJS.

Feltet "imports" i package.json definerer interne psevdopakker som kun kan importeres fra innenfor din faktiske pakke. Definer en intern importspesifikator, slik som #package.json i package.json:

{
  "type": "module",
  "exports": "./dist/index.js",
  "imports": {
    "#package.json": "./package.json"
  }
}

Aktiver TypeScript-støtte for ES-moduler og JSON-importer i tsconfig.json:

{
  "compilerOptions": {
    "module": "nodenext",
    // "moduleResolution" defaults to "nodenext", just made explicit here
    "moduleResolution": "nodenext",
    "resolveJsonModule": true,
  }
}

Til slutt, importer #package.json fra TypeScript-modulen din:

import packageJson from '#package.json' assert { type: 'json' };

console.log(packageJson);

Relaterte artikler