2

TypeScript e create-react-app: Importando uma lib em TypeScript no contexto de u...

 2 years ago
source link: https://dev.to/eduardojm/typescript-e-create-react-app-importando-uma-lib-em-typescript-no-contexto-de-um-monorepo-sem-transpilar-para-javascript-cbi
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Primeiro, precisamos entender o contexto ao qual pretendo desenvolver esse texto. Suponhamos que você tenha um monorepo (nesse caso, em específico, usando o yarn workspaces) com um projeto em React (criado utilizando o create-react-app) e um projeto de um sdk com chamadas para uma api qualquer, feito em TypeScript. O Objetivo principal é: importar o pacote sdk no app React sem a necessidade de transpilar/compilar o projeto sdk em toda alteração.

Disclaimer

Esse é um texto escrito com uma motivação em específica: quando precisei fazer isso não encontrei nenhum material parecido e isso acabou me custando algum tempo de pesquisa e, portanto, resolvi deixar a solução encontrada registrada.

Estrutura do Projeto

Como já mencionado no primeiro parágrafo, a estrutura do projeto, utilizando o yarn workspaces é basicamente:

O pacote sdk contém apenas um arquivo index.ts com uma função que faz uma requisição para uma api pública (de exemplo):

import axios from 'axios';

export interface CatImageItem {
    id: number;
    url: string;
    webpurl: string;
    x: number;
    y: number;
}

export async function getCat(): Promise<CatImageItem> {
    const result = await axios.get<CatImageItem>('https://thatcopy.pw/catapi/rest/');
    return result.data;
}
Enter fullscreen modeExit fullscreen mode

Com o package.json:

{
  "name": "@monorepo/sdk",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "typescript": "^4.4.4"
  },
  "dependencies": {
    "axios": "^0.24.0"
  }
}
Enter fullscreen modeExit fullscreen mode

my-app

Já o app em react é, somente, uma chamada do método exportado pelo sdk de exemplo:

import React, { useEffect, useState } from 'react';
import { getCat, CatImageItem } from '@monorepo/sdk';

function App() {
    const [cat, setCat] = useState<CatImageItem | null>(null);

    useEffect(() => {
        async function fetchCat() {
            const cat = await getCat();
            setCat(cat);
        }
        fetchCat();
    }, []);

    return (
        <div className="App">
            <h1>My Cat</h1>
            {cat && (
                <img src={cat.url} style={{ maxWidth: 400 }} />
            )}
        </div>
    );
}

export default App;
Enter fullscreen modeExit fullscreen mode

O Problema

Ao tentar rodar o projeto com os itens acima, obtemos de cara o erro:

Olhando para o webpack.config.js do create-react-app, onde é definido a regra do webpack para utilizar o babel para processar os arquivos TypeScript (aqui), possui a propriedade include que garante que só os arquivos dentro do app sejam processados por aquela regra, então o que acontece é que o nosso pacote simplesmente não é processado:

{
    test: /\.(js|mjs|jsx|ts|tsx)$/,
    include: paths.appSrc,
    loader: require.resolve('babel-loader'),
    // ...
Enter fullscreen modeExit fullscreen mode

A Solução

Existem duas soluções básicas: a primeira é ejetar o app, porém não quero entrar nessa pois é um tanto quanto trabalhosa. A segunda é utilizar o pacote react-app-rewired para rodar o nosso app e, portanto, poder reconfigurar o webpack. Então, primeiro, vamos instalar o pacote:

yarn workspace @monorepo/my-app add -D react-app-rewired
Enter fullscreen modeExit fullscreen mode

No package.json do my-app precisamos alterar alguns scripts para:

{
  //...
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  },
  //...
}
Enter fullscreen modeExit fullscreen mode

Agora, precisamos criar um arquivo config-overrides.js na raiz do projeto my-app, onde vamos remover aquele include para permitir que todo arquivo TypeScript seja processado pelo nosso projeto:

module.exports = {
    webpack: function(config, env) {
        config.module.rules[1].oneOf[2].include = undefined;
        return config;
    }
};
Enter fullscreen modeExit fullscreen mode

O arquivo acima vai sobreescrever a configuração do webpack, alterando, na segunda regra de modules, o terceiro item do array oneOf, mudando o include para undefined, ou seja, dizendo que o include não estará mais presente ali.

Agora, ao rodar o projeto, obtemos o nosso gatinho, sem nenhum erro:

Exemplo da aplicação rodando exibindo o gatinho

O repositório pode ser acessado clicando aqui.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK