Leveraging NPM workspaces in combination with Expo and Typescript

I'm struggling to incorporate NPM 7 workspaces into a Typescript Expo project. The goal is to maintain the standard Expo structure, with the root App.tsx file, while segregating certain code sections into workspaces.

I'm facing challenges compiling the TS code within the workspaces. Despite numerous attempts to configure the TS and/or Webpack settings, I haven't been successful. Here's a simplified file layout to replicate the issue:

package.json
tsconfig.json
App.tsx
/packages
  /core
    index.ts
    package.json

The crucial part of the main ./package.json

{
  "main": "node_modules/expo/AppEntry.js",
  "workspaces": [
    "packages/*"
  ],
  "scripts": {...},
  "dependencies": {...},
  "devDependencies": {...},
  "private": true
}

The minimal ./tsconfig.json

{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true
  }
}

The simple ./packages/core/package.json

{
  "name": "core",
  "private": true
}

In ./packages/core/index.ts, there's just an exported log() function for demonstration purposes

export function log(s: string) {
  console.log(s)
}

Lastly, in ./App.tsx, the function is imported and called

import { log } from 'core'
log('hello')
//...

Compilation Issue

When attempting to build for web (e.g., using expo build:web), the following error occurs:

✖ Expo Webpack
  Compiled with some errors in 1.62s

Failed to compile.

[path]/node_modules/core/index.ts 1:21
Module parse failed: Unexpected token (1:21)
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
> export function log(s: string) {
|   console.log(s)
| }

This error is expected since the /node_modules directory is intentionally excluded.

Therefore, my query is what measures should be taken to successfully compile the workspaces code? Ideally, it should be seamlessly integrated, akin to regular project files without precompilation. Is this attainable?

Failed Resolution Attempts

I primarily focused on altering the tsconfig to resolve the issue, although its impact remains questionable

#1

Tried adding

"include": ["./node_modules/core"],
in ./tsconfig.json. However, it proved ineffective as the error persisted.

#2

Created /packages/core/tsconfig.json containing the composite option:

{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true,
    "composite": true
  }
}

Referenced this in the root tsconfig.json:

{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true
  },
  "references": [{ "path": "./node_modules/core" }]
  // or even with:
  "references": [{ "path": "./packages/core" }]
}

Yet, these efforts didn't yield any positive outcome as the same error persisted.

Your assistance is greatly appreciated.

Answer №1

I managed to come up with a solution that, while not perfect, is getting the job done. I simply configured Webpack to compile my packages/* (as symbolic links in the node_modules directory).

To begin, I installed:

$ npm i -D ts-loader@8 <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0d7a686f7d6c6e664d3923393e3d">[email protected]</a>

Paying attention to versions is crucial here to maintain consistency with Expo 41.

In addition, I modified the package name (in /packages/core/package.json) to something like @workspace/core, ensuring all custom packages are located within the node_modules/@workspace directory.

This adjustment also simplifies our configuration.

Executing $ expo customize:web is essential for tweaking the webpack configuration. This command will generate a webpack.config.js file.

The configuration can be personalized as shown below:

const createExpoWebpackConfigAsync = require('@expo/webpack-config')

module.exports = async function (env, argv) {
  const config = await createExpoWebpackConfigAsync(env, argv)

  config.module.rules = [
    {
      // Using the @workspaces prefix eases the identification of renamed packages.
      test: /node_modules\/@workspaces\/(.*)\.ts$/, 
      use: [
        {
          loader: 'ts-loader',
          // Enabling ts-loader to compile within node_modules
          options: { allowTsInNodeModules: true },
        },
      ],
    },
    ...config.module.rules,
  ]

  return config
}

There may be a more streamlined solution out there, but for now, this setup will suffice.

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

Restricting the number of mat-chips in Angular and preventing the input from being disabled

Here is my recreation of a small portion of my project on StackBlitz. I am encountering 4 issues in this snippet: I aim to restrict the user to only one mat-chip. I attempted using [disabled]="selectedOption >=1", but I do not want to disable ...

How do I create a generic function in TypeScript that adjusts its behavior based on the input argument?

Is there a way to create a generic function that can accept generic arguments like Data<string, number>? Take a look at this code snippet: interface Data<T,R> { a:T; c:R; } function foo( data: Data<string, number> ){ return t ...

Define the state of an object using Parent and Children classes following the application of a filter

Within Angular 8, I am dealing with an Observable: let parents: Observable<Parent[]>; The classes Parent and Child are defined as follows: class Parent { id: number; name: string; children: Child[]; } class Child { id: number; name: str ...

The Reactjs dependency tree could not be resolved

In my current project, I've been attempting to integrate react-tinder-card. After running the command: npm install --save react-tinder-card I encountered this error in my console: npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency ...

The error message received states: "materialize-css Uncaught TypeError: Vel is not defined as

My current setup involves webpack as the bundler/loader, and I've successfully loaded materialize css (js/css). However, when attempting to use the toast feature, an error is thrown: Uncaught TypeError: Vel is not a function The library is being inc ...

Navigating between different views and pages within Angular FullCalendar can be achieved by utilizing the appropriate handlers for next,

After successfully integrating Angular fullCalendar into my Angular project and displaying events that I can click on, I found myself stuck when trying to handle clicks on the next and prev buttons as well as view changes. Unfortunately, the official docum ...

error message: could not find babel-node

Currently, I am in the process of creating a react application from the ground up using yarn instead of npm due to its faster speed. I have intentionally decided not to use the create-react-app command in order to build everything from scratch. For my web ...

The intricate field name of a TypeScript class

I have a TypeScript class that looks like this - export class News { title: string; snapshot: string; headerImage: string; } In my Angular service, I have a method that retrieves a list of news in the following way - private searchNews(sor ...

Use a spy to mock a component method using karma and jasmine

Encountering this error message during testing: ERROR: 'Error during cleanup of component' The issue stems from the following code snippet : ngOnDestroy(){ methodCallToMock() } To address this, I need to mock the methodCallToMock() functi ...

Is it possible to conceal dom elements within an ng-template?

Utilizing ng-bootstrap, I am creating a Popover with HTML and bindings. However, the ng-template keeps getting recreated every time I click the button, causing a delay in the initialization of my component. Is there a way to hide the ng-template instead? ...

Creating a nested type using template literal syntax

Given a two-level nested type with specific properties: export type SomeNested = { someProp: { someChild: string someOtherChild: string } someOtherProp: { someMoreChildren: string whatever: string else: string } } I am looking ...

What is the reason for a type narrowing check on a class property failing when it is assigned to an aliased variable?

Is there a way to restrict the type of property of a class in an aliased conditional expression? Short: I am trying to perform a type narrowing check within a class method, like this._end === null && this._head === null, but I need to assign the r ...

Utilize Node JS HTML-PDF to generate distinctive header and footer designs for the initial and final pages

Just starting out with node js and looking to have separate headers and footers for the first and last pages in my project, I've tried using the html-pdf module but it's not behaving as expected when following their provided code. Could someone ...

When using Angular 2, the `onYouTubeIframeAPIReady` function may not be able to locate the `YT` name, even if it is defined on the

As part of my efforts to track when a user pauses a YouTube video (link to the question), I have been utilizing the onYouTubeIframeAPIReady callback method in Angular2. I am facing similar challenges as discussed here and here. Following the recommendati ...

Utilizing Angular 2's pipe functionality with dynamic parameters

Currently I am diving into Angular2/Ionic2 and trying to grasp the concept of Pipes. Everything was going smoothly until I faced a roadblock in the form of a specific problem related to temperatures. Let me illustrate my issue with an example involving te ...

Using curly braces as function parameters in Typescript

While exploring the content metadata component of Alfresco ADF, I came across this typescript function that has left me puzzled: private saveNode({ changed: nodeBody }): Observable<Node> { return this.nodesApiService.updateNode(this.node.id, nodeB ...

Can you point me to the directory where the node_modules can be found in a React Native project?

I'm pondering on the functionality of npm modules in a basic JavaScript application. Typically, I have a package.json file and once I run 'npm install', a node_modules folder appears. Now, when it comes to React Native, how does this proces ...

Crafting a model for arrays of objects - a guide to perfection

Why am I experiencing errors in the console when trying to set the API return to a variable? How can this issue be resolved? This is my TypeScript code: public myData = new myDataModel(); getData(){ this.myCoolService.getDataAPI() .subscribe(( ...

How does npm facilitate the relocation of a script to the bin directory?

I am looking to make my code exclusively downloadable via git. Is there a way to transfer a script to the bin without using npm so it can be accessed without typing the entire file path and ./? For example, instead of $ ./do/this/really/long/script.sh. I ...

Issue: Loading ES Module in MikroOrm and Typescript requires the use of import statement

My attempt to set up a mikrorm configuration for my postgres DB is hitting a snag when I try to run my run-dev script. The issue stems from an ESM compilation error: > yarn dev Error: Must use import to load ES Module: C:\Users\Itay&b ...