Import methods without using relative path syntax

Viewed 693

I want to know more about monorepo projects. I have a repository named dnd which is using Yarn workspaces with Lerna. The repository contains two main directories.

packages/core
packages/app

The core directory contains utility methods and the app directory is using create-react-app boilerplate. Now here is the thing I want to use my utility methods in the main app components something like this.

import { concat } from '@dnd/core';

Currently, I am importing something like this

import { concat } from '../../../core/lib/utils';

I have to traverse the relative path with this ugly syntax. Now the path is correct but create-react-app throwing an error.

Module not found: You attempted to import ../../../core/lib/utils which falls outside of the project src/ directory. Relative imports outside of src/ are not supported.

Is there a way we can simply import our core directory methods inside our app components with something like this.

import { concat } from '@dnd/core';

Repositry: Link

dnd/package.json:

{
  "name": "root",
  "private": true,
  "scripts": {
    "bootstrap": "lerna bootstrap --use-workspaces"
  },
  "devDependencies": {
    "lerna": "^3.21.0"
  },
  "workspaces": [
    "packages/*"
  ]
}

dnd/lerna.json:

{
  "useWorkspaces": true,
  "packages": [
    "packages/*"
  ],
  "version": "0.0.0"
}

dnd/packages/core/package.json:

{
  "name": "@dnd/core",
  "version": "0.0.0",
  "description": "> TODO: description",
  "homepage": "",
  "license": "ISC",
  "main": "lib/core.js",
  "directories": {
    "lib": "lib",
    "test": "__tests__"
  },
  "files": [
    "lib"
  ],
  "scripts": {
    "test": "echo \"Error: run tests from root\" && exit 1"
  }
}

dnd/packages/core/lib/utils.js:

export const concat = (...args) => {
    return ''.concat(...args);
};

dnd/packages/app/package.json:

{
  "name": "@dnd/rain",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "@testing-library/user-event": "^7.1.2",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-scripts": "3.4.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

dnd/packages/app/src/components/App.js:

import React from 'react';
import { concat } from '../../../core/lib/utils';

const App = () => {
  return (
    <div className="App">
      { concat('Hello', 'World', 'Culture') }
    </div>
  );
};

export default App;
1 Answers

Yes you can.

Disclaimer: I am typing this on the fly (without testing) based on my limited experience on my own monorepo project.

It should give you a great starting point nontheless.

Idea

  1. You would need to compile packages/core using microbundle or rollup into a bundle.

  2. Then you want to include that bundled packages/core as a dependency in packages/app

Setup

packages/core/package.json

  • microbundle is a devDependency as we only require it during development.
{
  "name": "@dnd/core",
  "scripts": {
    "dev": "microbundle watch lib/*.js --target node"
  },
  "source": "lib/index.js", // entry point into @dnd/core
  "main": "dist/index.js",  // the compiled file/bundle that @dnd/app will reference 
  "devDependencies": [
   "microbundle": "^0.13.0"
  ]
}

packages/app/package.json

We add our compiled core package into package/app

{
  "name": "@dnd/app"
  "dependencies": [
    "@dnd/core": 0.0.0
  ]

packages/core/lib/index.js

  • Create an index.js file that will be an export of all your lib functions
  • This will be your entry point for @dnd/core
export * from './utils.js'
export * from './other-utils.js'

Before Development

Before we start development, we will need to "install" the new depedencies.

lerna bootstrap

Then run the npm dev script

# this will run the npm dev script only for packages/core
cd packages/core
npm run dev

or

# from the root of your project (inside /dnd folder)
# this will do a `npm run dev` equivalent inside all your packages
lerna run dev

This will tell microbundle to watch for changes inside packages/core/lib/index.js

Whenever you change code inside packages/core/lib/utils.js, it will recompile the code into packages/core/dist/index.js

Usages of @dnd/core inside @dnd/app

Now we can import our functions from core in our app source code.

import React from 'react';
import { concat } from '@dnd/core';

const App = () => {
  return (
    <div className="App">
      { concat('Hello', 'World', 'Culture') }
    </div>
  );
};

export default App;
Related