I'm trying to use NPM 7 workspaces within a Typescript Expo project.
For now I want to keep the normal Expo structure (with the root App.tsx
file), but I want to isolate some parts of the code in workspaces.
I have issues to compile the TS code within the workspaces. I tried many things to configure the TS and/or Webpack configs but without success. So here is the minimal file structure to reproduce it:
package.json
tsconfig.json
App.tsx
/packages
/core
index.ts
package.json
Here is the relevant part of the root ./package.json
{
"main": "node_modules/expo/AppEntry.js",
"workspaces": [
"packages/*"
],
"scripts": {...},
"dependencies": {...},
"devDependencies": {...},
"private": true
}
The ./tsconfig.json
is the bare minimum
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true
}
}
The ./packages/core/package.json
is very simple as well
{
"name": "core",
"private": true
}
And the ./packages/core/index.ts
simply exports a log()
function for this example
export function log(s: string) {
console.log(s)
}
Finally, in ./App.tsx
, is simply imports the function an try calling it
import { log } from 'core'
log('hello')
//...
When I try to build for the web (with expo build:web
for instance) I have the following error
ā 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)
| }
I'm not surprised because the /node_modules
directory is willingly excluded.
So my question is what do I need to do in order to get the workspaces code compiled? I'd like it to work on the fly (ie: not precompiled) just like if it was some regular files in my project. Is it doable at all?
I mainly tried to tweak the tsconfig to get it to work (although I kind wonder if it changes anything)
I tried adding "include": ["./node_modules/core"],
in ./tsconfig.json
. It had no effect (same error).
I tried to create the following /packages/core/tsconfig.json
with the composite option:
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"composite": true
}
}
And referenced it in the root tsconfig.json
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true
},
"references": [{ "path": "./node_modules/core" }]
// or even with:
"references": [{ "path": "./packages/core" }]
}
It had no effect (same error).
Thanks a lot
I came up with a solution that I'm not super happy with but at least it's working. It's actually quite straightforward, I simply configured Webpack to compile my packages/*
(as symlinks in the node_modules
.
So first I installed:
$ npm i -D ts-loader@8 webpack@4.430
Versions are important here to be consistent with the webpack version used by Expo 41.
I also renamed my package name (in /packages/core/package.json
) to something like @workspace/core
so that all my custom packages are inside the node_modules/@workspace
directory.
It will also make our config simpler.
Running $ expo customize:web
is necessary to customize the webpack config. It will produce a webpack.config.js
.
It can be customised as follow:
const createExpoWebpackConfigAsync = require('@expo/webpack-config')
module.exports = async function (env, argv) {
const config = await createExpoWebpackConfigAsync(env, argv)
config.module.rules = [
{
// this is why I renamed my packages with the @workspaces prefix.
test: /node_modules\/@workspaces\/(.*)\.ts$/,
use: [
{
loader: 'ts-loader',
// force ts-loader to compile in node_modules
options: { allowTsInNodeModules: true },
},
],
},
...config.module.rules,
]
return config
}
I wouldn't be surprised if there was a cleaner solution. But for now I'll stick to it