Tengo un monorepo que usa espacios de trabajo de hilo que tiene 2 proyectos Next.js.
apps ┣ app-1 ┗ app-2 app-1 necesita importar componentes de app-2 . Para hacer esto, agrego el proyecto app-2 como una dependencia y establezco la ruta en nuestra app-1 tsconfig así:
app-1 package.json { "name": "@apps/app-1", "version": "0.1.0", "private": true, "dependencies": { "@apps/app-2": "workspace:*", } } app-1 tsconfig.json { "compilerOptions": { "baseUrl": "./src", "paths": { "@apps/app-2/*": ["../../app-2/src/*"], "@apps/app-2": ["../../app-2/src"] } } } Esto funciona bien, sin embargo, el problema ocurre cuando un componente en app-2 importa otros componentes como import Component from "components/Component" .
app-1 no sabe cómo resolverlo y está buscando components/Components dentro de su propia carpeta src que no existe. Si ese mismo componente se importara como este import Component from ../../Component , se resolvería correctamente. Para solucionar esto, configuré otra ruta dentro del archivo tsconfig de app-1 para resolverlo manualmente. Ahora mi tsconfig parece
app-1 tsconfig { "compilerOptions": { "baseUrl": "./src", "paths": { "components/*": ["../../app-2/src/components/*"], // new path resolves absolute urls from app-2 "@apps/app-2/*": ["../../app-2/src/*"], "@apps/app-2": ["../../app-2/src"] } } } Sin esa línea de texto, intentar desarrollar o compilar el proyecto app-1 genera un Type error: Cannot find module 'components/Component' or its corresponding type declarations. No quiero resolverlo manualmente de esta manera porque app-1 podría querer su propia carpeta de components algún día y se resolvería erróneamente en la carpeta de componentes de la app-2 .
Parece un problema de mecanografiado basado en el error, pero no puedo decir si tal vez tenga algo que ver con webpack/babel o con enlaces simbólicos en nuestros node_modules
La solución ideal es cambiar algo con nuestra configuración o cargadores y hacer que estas rutas se resuelvan como cabría esperar.
next.js carga tsconfig.json para webpackConfig.resolve . Ver: 
Cuando un componente en app-2 importa otros componentes como import Component from "components/Component" , webpack resuelve los components/Component de acuerdo con app-1/tsconfig.json .
Solución: agregue un resolve plugin para app-2 .
app-1/tsconfig.json : { //... "compilerOptions":{ //... "paths": { "@apps/*": ["../app-2/*"], "components/*": ["./components/*"] }, } }app-2/tsconfig.json : { //... "compilerOptions":{ //... "paths": { "components/*": ["./components/*"] }, } }app-1/next.config.js : const path = require("path"); // fork from `@craco/craco/lib/loaders.js` function getLoaderRecursively(rules, matcher) { let loader; rules.some((rule) => { if (rule) { if (matcher(rule)) { loader = rule; } else if (rule.use) { loader = getLoaderRecursively(rule.use, matcher); } else if (rule.oneOf) { loader = getLoaderRecursively(rule.oneOf, matcher); } else if (isArray(rule.loader)) { loader = getLoaderRecursively(rule.loader, matcher); } } return loader !== undefined; }); return loader; } const MyJsConfigPathsPlugin = require("./MyJsConfigPathsPlugin"); const projectBBasePath = path.resolve("../app-2"); const projectBTsConfig = require(path.resolve( projectBBasePath, "tsconfig.json" )); module.exports = { webpack(config) { const projectBJsConfigPathsPlugin = new MyJsConfigPathsPlugin( projectBTsConfig.compilerOptions.paths, projectBBasePath ); config.resolve.plugins.unshift({ apply(resolver) { resolver .getHook("described-resolve") .tapPromise( "ProjectBJsConfigPathsPlugin", async (request, resolveContext) => { if (request.descriptionFileRoot === projectBBasePath) { return await projectBJsConfigPathsPlugin.apply( resolver, request, resolveContext ); } } ); }, }); // get babel-loader const tsLoader = getLoaderRecursively(config.module.rules, (rule) => { return rule.test?.source === "\\.(tsx|ts|js|mjs|jsx)$"; }); tsLoader.include.push(projectBBasePath); return config; }, };MyJsConfigPathsPlugin.js : // fork from `packages/next/build/webpack/plugins/jsconfig-paths-plugin.ts` const path = require("path"); const { // JsConfigPathsPlugin, pathIsRelative, matchPatternOrExact, isString, matchedText, patternText, } = require("next/dist/build/webpack/plugins/jsconfig-paths-plugin"); const NODE_MODULES_REGEX = /node_modules/; module.exports = class MyJsConfigPathsPlugin { constructor(paths, resolvedBaseUrl) { this.paths = paths; this.resolvedBaseUrl = resolvedBaseUrl; } async apply(resolver, request, resolveContext) { const paths = this.paths; const pathsKeys = Object.keys(paths); // If no aliases are added bail out if (pathsKeys.length === 0) { return; } const baseDirectory = this.resolvedBaseUrl; const target = resolver.ensureHook("resolve"); const moduleName = request.request; // Exclude node_modules from paths support (speeds up resolving) if (request.path.match(NODE_MODULES_REGEX)) { return; } if ( path.posix.isAbsolute(moduleName) || (process.platform === "win32" && path.win32.isAbsolute(moduleName)) ) { return; } if (pathIsRelative(moduleName)) { return; } // If the module name does not match any of the patterns in `paths` we hand off resolving to webpack const matchedPattern = matchPatternOrExact(pathsKeys, moduleName); if (!matchedPattern) { return; } const matchedStar = isString(matchedPattern) ? undefined : matchedText(matchedPattern, moduleName); const matchedPatternText = isString(matchedPattern) ? matchedPattern : patternText(matchedPattern); let triedPaths = []; for (const subst of paths[matchedPatternText]) { const curPath = matchedStar ? subst.replace("*", matchedStar) : subst; // Ensure .d.ts is not matched if (curPath.endsWith(".d.ts")) { continue; } const candidate = path.join(baseDirectory, curPath); const [err, result] = await new Promise((resolve) => { const obj = Object.assign({}, request, { request: candidate, }); resolver.doResolve( target, obj, `Aliased with tsconfig.json or jsconfig.json ${matchedPatternText} to ${candidate}`, resolveContext, (resolverErr, resolverResult) => { resolve([resolverErr, resolverResult]); } ); }); // There's multiple paths values possible, so we first have to iterate them all first before throwing an error if (err || result === undefined) { triedPaths.push(candidate); continue; } return result; } } };puede usar la configuración de babel de la siguiente manera.
Use el complemento de resolución de módulos.
Para instalar: yarn add -D babel-plugin-module-resolver
y siga este archivo de configuración.
module.exports = { presets: [], //Keep your preset as it is plugins: [ [ 'module-resolver', { root: ['./src'], extensions: ['.js', '.jsx', '.json', '.svg', '.png', '.tsx'], // Note: you do not need to provide aliases for same-name paths immediately under root alias: { "@apps/app-2": '../../app-2/src', }, }, ], ], };