Reputation: 1162
Here is what I want to achieve.
I want to make a package that I will use to share typescript interface, or common config that will be shared between my front-end (react) and my back-end (nestjs)
I have created project called "shared" and made a link in my package.json.
Like so : "shared": "file:../shared",
It works great my React, where can use my interface or anything from "shared" without any error !
I did the same in my nestjs project, there is not error in the editor and I can see the shared package in the node_modules. But when I compile the project, it fails with :
Error: Cannot find module 'shared/interfaces/user'
So I guess the problem comes from something in my nestjs conf or webpack... But I don't know what.
tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"moduleResolution": "node",
},
}
webpack-hmr.config.js
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const { RunScriptWebpackPlugin } = require('run-script-webpack-plugin');
module.exports = function (options) {
return {
...options,
entry: ['webpack/hot/poll?500', options.entry],
watch: true,
externals: [
nodeExternals({
allowlist: ['webpack/hot/poll?500'],
}),
],
plugins: [
...options.plugins,
new webpack.HotModuleReplacementPlugin(),
new RunScriptWebpackPlugin({ name: options.output.filename }),
new webpack.WatchIgnorePlugin( {paths: [/\.js$/, /\.d\.ts$/] }),
],
};
};
If you have any idea :) Thanks guys !
Upvotes: 6
Views: 5542
Reputation: 800
What I usually do in my multi-component backend applications, e.g. app server, Kafka consumers, k8s cron jobs, etc., is that I use a so-called monorepo structure. I assume you could do the same for your frontend + backend project. I would have the following directory structure in my projects:
- apps
- server
- package.json
- tsconfig.json
- internal-kafka-events-consumer
- package.json
- tsconfig.json
- sqs-consumer
- package.json
- tsconfig.json
- resource-deleter-job
- package.json
- tsconfig.json
- packages
- resource-repo
- package.json
- tsconfig.json
- resource-validator
- package.json
- tsconfig.json
- package.json
- tsconfig.json
- tsconfig-base.json
Apps
are private npm packages representing the components and packages
are also private npm package representing some re-usable libraries across the apps
.
In your case, both the frontend and backend pieces would be separate components in the apps
directory. To make this repository a monorepo, you can use yarn workspaces
. I will describe the important bits in the tsconfig.json
and package.json
files.
// package.json
{
"private": true,
"workspaces": {
"packages": [
"packages/*",
"apps/*"
]
}
}
The package.json
file in your root project is the one that tells yarn that you're going to work with a workspace and which directories should be considered workspaces.
// tsconfig.json
{
"files": [],
"include": [],
"references": [
{ "path": "./apps/server" },
{ "path": "./apps/internal-kafka-events-consumer" },
{ "path": "./apps/sqs-consumer" },
{ "path": "./apps/resource-deleter-job" },
{ "path": "./packages/resource-repo" },
{ "path": "./packages/resource-validator" }
]
}
The tsconfig.json
file in your root holds references to all the typescript projects in your repository. This so that if you run tsc --build
from the root, it can build all your subprojects at once, and that it can import modules from the referenced projects properly.
I also like to use a tsconfig-base.json
file to hold the shared configuration, i.e. all the TS configuration that is the same for all the apps
and packages
:
// tsconfig-base.json
{
"compilerOptions": {
... // compiler options shared by TS sub-projects
}
}
Assume that the server
project depends on both resource-repo
and resource-validator
packages. The resource-repo
's package.json file would look the following way:
// packages/resource-repo/package.json
{
"name": "@my-project/resource-repo",
"private": true,
"version": "1.0.0"
}
The tsconfig.json
file for the package would look this way:
// packages/resource-repo/tsconfig.json
{
"extends": "../../tsconfig-base.json",
"compilerOptions": {
... // project-specific compiler options
}
}
The apps/server/package.json
would look the following way:
// apps/server/package.json
{
"private": true,
"dependencies": {
"@my-project/reource-repo": "1.0.0",
"@my-project/resource-validator": "1.0.0"
}
}
And its tsconfig.json
file would look this way:
// apps/server/tsconfig.json
{
"extends": "../../tsconfig-base.json",
"compilerOptions": {
... // project-specific compiler options
},
"references": [
{ "path": "../../packages/resource-repo" },
{ "path": "../../packages/resource-validator" }
]
}
I don't have much experience with TypeScript front-end projects but I think that given this monorepo structure with the proper yarn workspace
and TS setup adding bundling via Webpack or similar should not be a big issue. A simple Google search for "webpack typescript monorepo" gave me this article.
Upvotes: 4
Reputation: 21309
You should display the code importing your module as well. The issue might be due to the import where the path should be relative to you file and not an absolute path. Same as: https://stackoverflow.com/a/54049216/532695
If you really want to have a shorter link, like import {myInterface} from @shared/myModule
you can setup path alias in your tsconfig.json
file.
{
...
"compilerOptions": {
"paths": {
"@shared/*": ["path/to/shared/folder/*"],
}
},
...
}
But this is only understood by ts or possibly ts-node. If you compile this using webpack, you might need to create an alias in its configuration as well:
...
resolve: {
alias: {
shared: path.resolve(__dirname,'path/to/shared/folder/')
}
}
...
Here is an article about how to setup aliases for webpack
Upvotes: 1