nestjs - No se resolvió la importación del paquete local de TypeScript
Aquí está lo que quiero lograr.
Quiero crear un paquete que usaré para compartir interfaces de TypeScript o configuraciones comunes que serán compartidas entre mi frontend (React) y mi backend (NestJS)
He creado un proyecto llamado "shared" y he hecho un enlace en mi package.json.
Así: "shared": "file:../shared",
¡Funciona muy bien en mi React, donde puedo usar mi interfaz o cualquier cosa de "shared" sin ningún error!
Hice lo mismo en mi proyecto de nestjs, no hay errores en el editor y puedo ver el paquete compartido en los node_modules. Pero cuando compilo el proyecto, falla con:
Error: No se puede encontrar el módulo 'shared/interfaces/user'
Así que supongo que el problema viene de algo en mi configuración de NestJS o webpack... Pero no sé qué.
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$/] }),
],
};
};
Si tienen alguna idea :) ¡Gracias chicos!
Respuesta #1
Lo que normalmente hago en mis aplicaciones de backend de múltiples componentes, por ejemplo, servidor de aplicaciones, consumidores de Kafka, trabajos cron en k8s, etc., es que utilizo una estructura denominada monorepo. Supongo que podrías hacer lo mismo para tu proyecto de frontend + backend. Tendría la siguiente estructura de directorios en mis proyectos:
- 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 son paquetes npm privados que representan los componentes y packages también son paquetes npm privados que representan algunas bibliotecas reutilizables a través de los apps. En su caso, tanto el frontend como el backend serían componentes separados en el directorio apps. Para convertir este repositorio en un monorepo, puede usar yarn workspaces. Describiré las partes importantes en los archivos tsconfig.json y package.json.
// package.json
{
"private": true,
"workspaces": {
"packages": [
"packages/*",
"apps/*"
]
}
}
El archivo package.json en la raíz de tu proyecto es el que le indica a yarn que vas a trabajar con un espacio de trabajo y qué directorios deben considerarse espacios de trabajo.
// 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" }
]
}
El archivo tsconfig.json en tu raíz contiene referencias a todos los proyectos de TypeScript en tu repositorio. Esto es para que si ejecutas tsc --build desde la raíz, pueda construir todos tus subproyectos de una vez, y para que pueda importar módulos de los proyectos referenciados correctamente.
También me gusta usar un archivo tsconfig-base.json para mantener la configuración compartida, es decir, toda la configuración de TS que es la misma para todas las apps y packages:
// tsconfig-base.json
{
"compilerOptions": {
... // compiler options shared by TS sub-projects
}
}
Suponga que el proyecto server depende de los paquetes resource-repo y resource-validator. El archivo package.json de resource-repo se vería de la siguiente manera:
// packages/resource-repo/package.json
{
"name": "@my-project/resource-repo",
"private": true,
"version": "1.0.0"
}
El archivo tsconfig.json para el paquete se vería de esta manera:
// packages/resource-repo/tsconfig.json
{
"extends": "../../tsconfig-base.json",
"compilerOptions": {
... // project-specific compiler options
}
}
El apps/server/package.json se vería de la siguiente manera:
// apps/server/package.json
{
"private": true,
"dependencies": {
"@my-project/reource-repo": "1.0.0",
"@my-project/resource-validator": "1.0.0"
}
}
Y su archivo tsconfig.json se vería de esta manera:
// apps/server/tsconfig.json
{
"extends": "../../tsconfig-base.json",
"compilerOptions": {
... // project-specific compiler options
},
"references": [
{ "path": "../../packages/resource-repo" },
{ "path": "../../packages/resource-validator" }
]
}
No tengo mucha experiencia con proyectos front-end en TypeScript, pero creo que dado esta estructura de monorepo con el yarn workspace adecuado y la configuración de TS, agregar bundling vía Webpack o similar no debería ser un gran problema. Una búsqueda simple en Google de "webpack typescript monorepo" me dio este artículo.
Respuesta #2
También deberías mostrar el código que importa tu módulo. El problema podría deberse a la importación, donde la ruta debería ser relativa a tu archivo y no una ruta absoluta. Igual que en: https://stackoverflow.com/a/54049216/532695
Si realmente quieres tener un enlace más corto, como import {myInterface} from @shared/myModule, puedes configurar un alias de ruta en tu archivo tsconfig.json.
{
...
"compilerOptions": {
"paths": {
"@shared/*": ["path/to/shared/folder/*"],
}
},
...
}
Pero esto solo lo entienden ts o posiblemente ts-node. Si compilas esto usando webpack, es posible que también necesites crear un alias en su configuración:
...
resolve: {
alias: {
shared: path.resolve(__dirname,'path/to/shared/folder/')
}
}
...
Aquí hay un artículo sobre cómo configurar alias para webpack
