javascript - Hur importerar man delad typescript-kod med create-react-app (utan att göra eject)?


Jag försöker uppnå delning av TypeScript-kod i en create-react-app i icke-eject-läge, men jag stöter på den ökända begränsningen att importer utanför src inte är tillåtna:

You attempted to import ../../common/src/lib.ts which falls outside of the project src/ directory. [...]

För fallet utan TypeScript har detta frågats och besvarats här, men jag kan inte få någon av lösningarna att fungera med TypeScript. Specifikt är problemen med de föreslagna lösningarna:

  • Att ställa in baseDir i ts-config.json: Här klagar create-react-app på: Ditt projekts baseUrl kan endast sättas till src eller node_modules. Create React App stöder för närvarande inte andra värden.

  • Metoder baserade på react-app-rewired: Mer lovande. Att inaktivera ModuleScopePlugin tar mig förbi felet "försökte importera utanför src", men problemet nu är att laddaren för TypeScript-filer inte samarbetar:

    Jag har verifierat att .ts själv är okej: Att kopiera till ./src och importera det därifrån fungerar bra.

    Jag har också lagt till mappen ../../common/src i includes-listan i ts-config.json.

    Min gissning är att webpack-loader-konfigurationen på något sätt har en regel som hindrar TypeScript-loaden från att transpilerar filer som inte matchar dess förväntade sökmönster. Hur kan detta åtgärdas med react-app-rewired?

  • Att skapa symboliska länkar till källorna fungerar inte heller - återigen med samma problem. Troligtvis eftersom webpack internt löser symboliska länkar och ser filen i en sökväg där de vanliga loader-reglerna inte gäller.

  • Eject-baserade lösningar: Jag skulle helst inte vilja ejecta för tillfället, men jag försökte och jag stöter i princip på samma problem igen.

Jag har också hittat andra frågor som verkade relaterade men inte svarade på problemet:

  • Skapa React-app + Typescript i monorepo koddelning: Låter i princip som samma fråga, men det är det inte, eftersom det handlar om fallet med en ejected React-app.
  • Dela kod mellan projekt som använder TypeScript och webpack: Tar också upp problemet med koddelning, men är inte specifikt för create-react-app, och jag vet inte om lösningen kan överföras till create-react-app, eftersom det kräver manuell kontroll av webpack-konfigurationen.

Att återanvända TypeScript-typningar i ett mono repo verkar vara ett ganska rimligt mönster. Missar jag något eller varför gör create-react-app detta så svårt?

För att reproducera: Min kod är i princip 100% vad du får från

npx create-react-app my-app --template typescript

med en import till extern tillagd.

Svar #1

Du kan använda craco (create-react-app config override) för att åsidosätta webpack-konfigurationen (abstraherad som en del av CRA) utan att ejecta.
Dessutom kan du använda ts-loader för att direkt referera icke-transpilerad ts-kod i externa projekt (t.ex. om du vill referera/använda delad kod/bibliotek som en del av ett mono-repo).

Om vi antar att din CRA-app finns i client-katalogen ser din projektstruktur ut som följer:

client/
|--src/
|--package.json
shared/
|--package.json
|--(ts files)
package.json

    cd client
    yarn add -D @craco/craco ts-loader
    
    1. skapa en craco.config.js-fil i client/ (CRA)-katalogen
    2. se till att craco.config.js har följande innehåll
    const path = require("path");
    
    module.exports = {
      webpack: {
        configure: webpackConfig => {
    
          // ts-loader is required to reference external typescript projects/files (non-transpiled)
          webpackConfig.module.rules.push({
            test: /\.tsx?$/,
            loader: 'ts-loader',
            exclude: /node_modules/,
            options: {
              transpileOnly: true,
              configFile: 'tsconfig.json',
            },
          })
          
          return webpackConfig;
        }
      }
    };
    
    1. Ersätt react-scripts-kommandon i client/package.json med craco
    /* client/package.json */
    
    "scripts": {
    -   "start": "react-scripts start",
    +   "start": "craco start",
    -   "build": "react-scripts build",
    +   "build": "craco build"
    -   "test": "react-scripts test",
    +   "test": "craco test"
    }
    

    Svar #2

    UPPDATERING: Eftersom react-app-rewired endast underhålls passivt och inte stödjer CRA-versioner 2+ (vi är tre huvudversioner senare vid tidpunkten för skrivandet), skulle jag inte längre rekommendera detta tillvägagångssätt.


    Efter flera timmar av experimenterande och att ha läst på GitHub-ärenden, har jag äntligen en fungerande lösning. Stort tack till BirukDmitry som gjorde detta mycket hjälpsamma inlägg på GitHub. Steg-för-steg-guide:

    1. Installera react-app-rewired och customize-cra

       npm i react-app-rewired customize-cra --save-dev
      
    2. Konfigurera react-app-rewird med en minimal config-overrides.js på detta sätt:

      const { removeModuleScopePlugin, override, babelInclude } = require("customize-cra");
      const path = require("path");
      
      module.exports = override(
        removeModuleScopePlugin(),        // (1)
        babelInclude([
          path.resolve("src"),
          path.resolve("../common/src"),  // (2)
        ])
      );
      

      Inställning (1) är liknande det som behövs för att komma runt import-outside-src-begränsningen i allmänhet (se länkad fråga).

      Inställning (2) är dock avgörande för att aktivera babel-transpilering (och därmed, inklusive TS-typeborttagning antar jag) för andra sökvägar också. Det är här du måste lägga till dina sökvägar från vilka du vill importera.

    3. Inga anpassningar behövs för tsconfig.json.

    4. Importera med relativa sökvägar, t.ex. import * as mymodule from '../../common/src/mymodule'.

    Relaterade artiklar