Bartek
Bartek

Reputation: 85

Importing JavaScript to TypeScript app with Lerna

I try to create a monorepo with 2 React packages:

I've got a basic lerna.json configuration

{
  "packages": ["packages/*"],
  "version": "1.0.0"
}

In the ui package I simply export one Button (from src/Button.jsx):

import React from 'react';

const Button = () => {
  return (
    <button>
      Start
    </button>
  )
}

export default Button;

I've bootstrapped app to use the ui package. Importing it inside the app causes the following error:

Failed to compile
/lerna-demo/packages/ui/src/Button.jsx 5:4
Module parse failed: Unexpected token (5:4)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| const Button = () => {
|   return (
>     <button>
|       Start
|     </button>

Is there a way to add a loader to lerna or app to fix the import?

EDIT

The project structure:

lerna-demo/
  - node_modules/
  - lerna.json
  - package.json
  - packages/
    - app (created using create-react-app)
      - ...
    - ui
      - node_modules/
      - package.json
      - yarn.lock
      - src/
        - Button.jsx

The way I import the Button component:

import React from 'react';
import logo from './logo.svg';
import './App.css';
import * as Button from 'ui/src/Button';

const App: React.FC = () => {

  return (
    <div className="App">
      <Button />
    </div>
  );
}

export default App;

Upvotes: 5

Views: 1153

Answers (2)

aboutaaron
aboutaaron

Reputation: 5389

I followed this answer and was able to import my separate standalone package in my lerna monorepo into my CRA typescript app by using craco and pointing the webpack config to my standalone package. I find this solution easier to manage instead of ejecting from your existing CRA config.

Given this structure:

├── package.json
├── packages
│   ├── app
│   ├── ui
└── yarn.lock

...here are the main steps:

  1. Install craco in app/package.json:
yarn add @craco/craco
  1. Create a craco.config.js file in the CRA project root
├── package.json
├── packages
│   ├── app
│   │   ├── src
│   │   └── craco.config.json <--- in CRA project root
│   │  
│   ├── ui
│   │   ├── src
│   
│   
└── yarn.lock
  1. Point the webpack config to the shared module
// project/app/craco.config.js
// ---
const path = require('path');
/**
 * ALlows us to edit create-react-app configuration
 * without ejecting.
 *
 *
 */
const { getLoader, loaderByName } = require('@craco/craco');
// Replace `components` with your folder's structure.
// Again, Here I'm showcasing my current project.
const absolutePath = path.join(__dirname, '../ui');
/**
 * This is extremely important if you're using a CI/CD to build and deploy 
 * your code!!! Read!
 * 
 * If you create a package with a namespace, such as name is @schon/components, you need
 * to add what I've commented down below. Otherwise Yarn will fail in CI/CD environments!!
 */
// const uiComponents = path.join(
//   __dirname,
//   './node_modules/ui',
// );

module.exports = {
  webpack: {
    configure: (webpackConfig, { env, paths }) => {
      // https://medium.com/frontend-digest/using-create-react-app-in-a-monorepo-a4e6f25be7aa
      const { isFound, match } = getLoader(
        webpackConfig,
        loaderByName('babel-loader'),
      );
      if (isFound) {
        const include = Array.isArray(match.loader.include)
          ? match.loader.include
          : [match.loader.include];
        // match.loader.include = include.concat(absolutePath, uiComponents);
        match.loader.include = include.concat(absolutePath);
      }
      return {
        ...webpackConfig,
        /**
         * Optionally, other webpack configuration details.
         */
        // optimization: {
        //   splitChunks: {
        //   },
        // },
      };
    },
  },
  
};

Upvotes: 0

Adidi
Adidi

Reputation: 5253

In your tsconfig.json created by CRA you will need to tell the compiler to include also the ui pacakge so in your example its something like that:

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react"
  },
  "include": [
    "src", "../ui/src",
  ]
}

Another option is to extend your base lerna tsconfig.json and there its already points to your package with path I guess - so from your CRA tsconfig.json just add this:

{
    "extends": "../../tsconfig.json",

and in base tsconfig.json u should define the alias name to the path: main tsconfig.json

{
    "compilerOptions": {
        "baseUrl": "./",
        "paths": {
            "ui/*": ["./packages/ui/*"],

Upvotes: 3

Related Questions