What is the best way to bundle a TypeScript package along with its dependencies for seamless integration with various Next.js projects on a local environment

Currently, I am immersed in a project with the following arrangement:

/         # repository root
/core     # A local, unpublished npm package used by both projectA and projectB
/projectA # A Next.js app
/projectB # Another Next.js app

In my setup, I generate a core-1.0.0.tgz file by running tsc && npm pack on /core/package.json and /core/tsconfig.json. The transpilation process seems to run smoothly without any errors, resulting in the expected transpiled .js code being present in the .tgz file.

/core/tsconfig.json

{
  "compilerOptions": {
    "lib": ["es2022", "DOM"],
    "module": "commonjs",
    "target": "es2022",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": ".compiled",
    "declaration": true,
    "jsx": "react"
  },
  "include": ["./index.ts"]
}

/core/package.json

{
  "name": "core",
  "version": "1.0.0",
  "engines": {
    "node": "18.15.0"
  },
  "main": "./.compiled/index.js",
  "types": "./.compiled/index.d.ts",
  "files":[
    "./.compiled/**"
  ],
  "scripts": {
    "test": "ts-node ./tests.ts",
    "build": "npm install && npm run test && tsc && npm pack"
  },
  "dependencies": {
    ...
    "@uppy/aws-s3": "3.3.1",
    ...
  }
  ...
}

To integrate the core package into projectA, I modify /projectA/package.json as follows:

...
"dependencies": {
  ...
  "core": "../core/core-1.0.0.tgz",
  ...
},
...

After executing npm install in /projectA, everything appears functional. Upon inspection of projectA/node_modules, not only do I see the transpiled JS files under the core folder, but also the dependencies from core have been installed into projectA/node_modules.

However, upon launching projectA, an error surfaces immediately related to a core import, stating:

Error: require() of ES Module C:\dev\repo\projectA\node_modules\@uppy\aws-s3\lib\index.js from C:\dev\repo\projectA\node_modules\core\.compiled\library\components\file-uploader\FileUploader.js not supported.
Instead change the require of index.js in C:\dev\repo\projectA\node_modules\core\.compiled\library\components\file-uploader\FileUploader.js to a dynamic import() which is available in all CommonJS modules.

@uppy/aws-s3 stands as one of the sub-dependencies of core. The error emanates from FileUploader.js, a transpiled version of FileUploader.ts React component designed as a UI wrapper for @uppy. Although understanding the error message, I feel slightly disoriented amidst the layers. Presumably, tweaking /core/tsconfig.json is necessary, yet pinpointing precisely what requires alteration remains elusive.

/core/.../FileUploader.ts

import UppyAwsS3 from '@uppy/aws-s3'
import Uppy, { SuccessResponse, UppyFile } from '@uppy/core'
import UppyDashboard from '@uppy/dashboard'
import cx from 'classnames'
import React from 'react'

interface Props {
  className?: string
  fullWidth: boolean
  ...
}

interface State {}

class FileUploader extends React.Component<Props, State> {
  ...
  render = () => {
    return (
      <div className={cx('FileUploader', this.props.className, this.props.fullWidth && 'FileUploader--fullWidth')}>
        ...
      </div>
    )
  }
}

export default FileUploader

Provided here is /projectA/tsconfig.json for reference:

{
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "jsx": "preserve",
    "lib": ["ES2022", "dom"],
  },
  "exclude": [
    "node_modules"
  ]
}

If anyone holds insights on establishing this shared TypeScript package, or notices discrepancies in my current setup, your assistance would be highly appreciated.

Edit: Advice has been given to refer to this question. However, my scenario involves TypeScript rather than plain JavaScript. My inquiry pertains to configuring tsc to compile the correct import syntax, allowing Next.js to utilize the shared FileUploader component from my core CommonJS package dependent on the ESM package @uppy/aws-s3.

Update: To aid in troubleshooting, I've prepared a basic sample project illustrating the error. It can be accessed here.

Answer №1

If you find yourself in need of a tool for managing and publishing multiple JavaScript/TypeScript packages within the same repository, consider using Lerna.

Lerna is a cutting-edge build system that offers speed and efficiency in handling various projects simultaneously.

In essence, Lerna specializes in overseeing multi-project packages.

By running 'lerna init', you can create a packages directory where your packages will reside. You can establish links between these packages through:
'lerna add shared --scope=packageA'

This command allows you to incorporate the 'shared' module into the 'packageA' node modules. By adding this dependency, you refer to the 'name' specified in the 'package.json' file of the shared module.

// Inside the package.json file of packageA:
  "dependencies": {
    "nameOfPackageJsonOfSharedModule": "^1.0.0",
  }

Upon completing these steps, you are now able to utilize the 'shared' module within 'packageA'.

// The 'nameOfPackageJsonOfSharedModule' represents the previously mentioned dependency
const share = require("nameOfPackageJsonOfSharedModule")

If integrating the module does not yield the desired outcome, consider building each package separately and referencing the build directory when importing modules. For example, if you have built the 'shared' module, include the following line in its package.json file:

// Upon importing this package, 'build/index.js' will be executed
"bin": "build/index.js"

Answer №2

To properly configure your project, make sure to include

"module": "commonjs"
in the compilerOptions. Depending on your specific setup, you may need to adjust other settings as well.

Alternatively, I recommend utilizing the TurboRepo setup, which is officially endorsed by Next.js. For a helpful reference, take a look at this template repository.

Check out some of the packages I have developed using this configuration:

Feel free to explore more possibilities!

Answer №3

Forget about npm and switch to pnpm instead! By packaging your dependencies, pnpm allows you to easily use them in multiple projects.

Visit pnpm's website for more information.

Answer №4

After a thorough investigation and various attempts, I successfully managed to compile and link my example without the need to install any extra packages or dependencies. It was truly a journey of trial and error, but I finally got everything up and running smoothly.

Here are the key changes that made it possible:

  • core/tsconfig.json
    • Following Icekid's recommendation, I switched compilerOptions.module and compilerOptions.target to ES6.
    • Included
      compilerOptions.moduleResolution: "Node"
      ; this step was crucial as TypeScript was not recognizing my import statements during the tsc run without it.
  • projectA/next.config.js
    • Added transpilePackages: ['core']. As highlighted in this discussion on Next.js quirks, I discovered that Next.js doesn't automatically transpile locally imported ES6 modules per the tsconfig.json. This required manual intervention for my custom local package 'core', unlike other packages like uppy/aws-s3 which were auto-transpiled without issues within projectA.

With these adjustments in place, everything fell into line seamlessly while all other settings remained unchanged.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

There appears to be an issue with the dynamic functionality of RouterLink in Angular 6

user-dashboard.html <ul class="nav flex-column"> <li class="nav-item"> <a class="nav-link" routerLink='/dashboard'>User Dashboard</a> </li> <li class="nav-item" *ngFor="let cat of categories; let i = in ...

What is the best way to exclude React.js source files from a fresh Nest.js setup?

My setup includes a fresh Nest.js installation and a directory named "client" with a clean create-react-app installation inside. Here is the project structure: ./ ...some Nest.js folders... client <- React.js resides here ...some more Nest.js fo ...

Exploring the functionality of API routing in Next.js

Seeking assistance from someone well-versed in Next.js as I encounter an issue while transitioning my express API routes to Next.js's internal API routes within pages, which appears to be a promising solution. The problem lies in getting it to functio ...

I'm having trouble with getStaticProps requiring a full URL, but I'm unable to provide one while building on

I'm currently facing an issue while attempting to deploy a Next.js project using Vercel. The problem arises when some API calls within the getStaticProps() function require access to the api folder located inside the pages folder of the Next.js direct ...

A guide on setting up Firestore rules using session cookies for secure authentication

For my next.js application, I incorporated session cookies using this guide. However, since there is no client user authentication, all firestore calls and permissions need to be managed within the server-side API. This brings up a question about the rele ...

Render function in Next.js did not yield anything

Recently, I came across the next.js technology and encountered an error. Can anyone help me solve this issue? What could be causing it?View image here import React from 'react' import Button from "../components/button" function HomePa ...

Why am I getting the "Cannot locate control by name" error in my Angular 9 application?

Currently, I am developing a "Tasks" application using Angular 9 and PHP. I encountered a Error: Cannot find control with name: <control name> issue while attempting to pre-fill the update form with existing data. Here is how the form is structured: ...

Developing a custom package containing personally crafted type definitions and importing them into the project

I'm in need of creating a private npm package specifically for custom type definitions (typedefs) that are hand-written d.ts files not generated by TypeScript. Since these are proprietary, they cannot be added to DefinitelyTyped. The folder structure ...

The Canvas .tsx file may have a null object and does not contain the getContext property within the type never

I'm currently working on developing a canvas component to integrate into my application. My ultimate goal is to enable users to draw on this canvas. The issue arises with the following line of code: const context = canvas.getContext("2d"); ...

Is there a way for me to retrieve the header values of a table when I click on a cell?

I have a project where I am developing an application for booking rooms using Angular 2. One of the requirements is to be able to select a cell in a table and retrieve the values of the vertical and horizontal headers, such as "Room 1" and "9:00". The data ...

What is the best way to asynchronously refresh Angular 2 (or 4) PrimeNg Charts?

Issue: How can PrimeNg Charts be updated asynchronously? Situation: I have a dropdown menu that should trigger a chart refresh based on the user's selection. I believed I had the solution figured out, understanding Angular change detection and reali ...

Using Lodash to Substitute a Value in an Array of Objects

Looking to update the values in an array of objects, specifically the created_at field with months like 'jan', 'Feb', etc.? One way is to loop through using map as demonstrated below. However, I'm curious if there's a more co ...

Tips on how to perform a server-side redirection to a different page in a Nextjs application without refreshing the page while maintaining the URL

I am working on a [slug].js page where I need to fetch data from an API to display the destination page. export async function getServerSideProps({ query, res }) { const slug = query.slug; try { const destination = await RoutingAPI.matchSlu ...

Seeking guidance on how to retrieve an image from Firebase storage using NextJs. Any suggestions on how to tackle this issue

Here is the code snippet we are currently utilizing in Nextjs to facilitate image downloads: const handleDownload = async (fileUrl: string) => { try { const storageRef = ref(storage, fileUrl); const downloadURL = await getDownloadURL(storageRe ...

Error encountered when trying to update tree structure in TypeScript with new data due to incorrect array length validation

I have encountered an issue with my tree data structure in TypeScript. After running the updateInputArray(chatTree); function, I am getting an “invalid array length” error at the line totalArray.push(iteratorNode.data);. Furthermore, the browser freeze ...

Issues with concealing the side menu bar in Vue.js

I've been attempting to conceal the side menu bar, with the exception of the hamburger icon when in the "expanded" state. Despite my efforts to modify the CSS code, I am still struggling to hide the small side menu bar. The following images represent ...

Unable to find solutions for all parameters needed by a CustomComponent within Angular

Whenever I attempt to compile the project for production, an error pops up: There is a problem resolving all parameters for EmployeeComponent in C:/.../src/app/employee/employee.component.ts: (?, ?, ?). Oddly enough, when I run the application, every ...

Formik QR code reader button that triggers its own submission

I recently developed a custom QR code reader feature as a button within the Formik component customTextInput.tsx, but I encountered an issue where clicking on the button would trigger a submission without any value present. The following code snippet show ...

Retrieve the instance from the provider using its unique key without needing to inject its dependencies

I created a custom class called PermissionManager, which takes a list of Voter interfaces as an input in its constructor. export interface Voter { vote(): bool; } export class PermissionManager { constructor(private readonly ...

Exploring the depths of friendship with React-Router V6 through recursive routes

I am currently facing an issue with my React-Router V6 implementation. The example I found for recursive routes in React-Router V5 is exactly what I need: However, after migrating to react-router-dom@6, the output is not as expected. import { Routes, ...