Typescript和Next.js仅包含来自外部模块根的另一个项目的 * 源代码 *?

rpppsulh  于 2023-11-18  发布在  TypeScript
关注(0)|答案(1)|浏览(125)

摘要

我有一个使用Typescript的封闭源代码Next.js站点。我试图从外部开源Next.js项目(我也拥有)导入一些React组件。我试图以一种共享node_modules的方式来完成这一任务,或者完全从封闭源代码项目中导入,并且当我修改外部项目中的文件时,我可以在Next.js中进行热重新加载。
我正在努力寻找tsconfig.jsonnext.config.js/webpack.config的组合,以实现我想要的。

问题详情

我有两个使用Typescript的Next.js项目:

  • “A”是闭源的。这是一个使用Typescript的Next.js站点。它是闭源的,因为这个站点有身份验证,安全性等。这个项目从项目B导入。
  • “B”是开源的。这个项目主要包含一个大型的Editor组件(想想一个代码编辑器)。我希望这个项目是开源的,因为我希望编辑器是开源的。为了方便,这个项目也是一个Next.js应用程序,这样你就可以在本地运行一个热重载的服务器来在编辑器上开发,而不需要访问闭源项目。

文件夹结构类似于

A/ (closed source)
  node_modules/
  tsconfig.json
  next.config.jstsx
  pages/
    index.tsx
B/ (open source)
  node_modules/
  tsconfig.json
  next.config.js
  components/
    Editor.tsx
  pages/
    index.tsx

字符串
最终,我想做的是,在项目A中能够从项目B导入文件,就像它们是项目A本地的一样:

import Editor from '../../B/components/Editor.tsx`


例如:

  • 当我在项目A中运行Next.js应用程序,并在项目B中修改Editor组件时,Next.js站点会热重载更改
  • Editor.tsx做类似import React from 'react';的事情时,它使用项目A的依赖项,以避免将两个版本的React捆绑到本地站点。

项目B中的Editor.tsx还导入了一些项目A中当前没有的Node模块,比如一些第三方React库。理想情况下,我希望在A中运行的Next.js站点能够弄清楚这些依赖项需要来自B/node_modules,而像React这样的 common 库来自A/node_modules,但我也可以强迫A安装这些相同的依赖项。
我在这方面遇到的麻烦是找到next.config.jstsconfig.json的正确组合来使其工作,我不清楚Next.js的热重新加载和加载如何与Typescript的外部依赖项相互作用。
我最近尝试的是把这个放在我的next.config.js中:

transpilePackages: [
    path.resolve(__dirname, '../B'),
  ],


在我的tsconfig.json中:

"references": [
    { "path": "../B" }
  ],


为了测试这是做什么我想,我rm -rf node_modules在项目B,以确保这些节点模块没有被使用。
但是在我的A站点中,当我导入../B/components/Editor.tsx时,我得到:

- error ../B/src/plugins/Editor.tsx
Module not found: Can't resolve 'react/jsx-dev-runtime'


这告诉我,Next.js试图从B/node_modules使用React,即使React安装在A/
我尝试过tsconfig的rootDirspaths的一些配置,以及next.config.jstranspileModules ',以及尝试修改webpack.config.ruleswebpack.config.snapshot.managedPathswebpack.config.watchOptions。但我不清楚所有这些如何一起发挥作用(或不发挥作用),特别是考虑到Typescript和Webpack的相互作用。
很抱歉问了这么长的问题,我试图列出我的假设,以防我有什么错,或者你看到一个创造性的解决方案来满足我的需求。

7rtdyuoh

7rtdyuoh1#

这是一个非常具体的请求。在我看来,有两个选项,但模块联合选项可能是你真正想要的......我会谈到这个。

选项1:软件包B是软件包A的依赖项在我看来,您上面的设置的问题是您试图将组件用作依赖项目B,B的根位于项目A的根之外.这不起作用,因为项目B的模块解析在项目B的node_modules目录内查找。

解决方法是将项目B放在项目A的node_modules中。
但是这种方法有一个问题,项目B是Next.js项目,所以它并没有真正配置为导出Editor组件。为了解决这个问题,你可以添加一个构建步骤,在项目B上运行tsc,然后在package.json中暴露编辑器,如下所示:

// package.json
{ ...
  "exports": {
    "./Editor": "./dist/path/to/Editor.js"
  }
...
}

字符串
然而,这是一种混乱海事组织,这导致我什么是'正常'的做法,这种情况将是.

选项1A:编辑器为共享模块

这就是大多数人处理在软件包之间共享组件的方法,也就是说,将编辑器移到它自己的仓库中。也许你有足够多的这样的情况,有一个共享组件库是有意义的,不管是什么情况,你会创建一个像MyComponentLib这样的软件包,它成为项目A和B的直接依赖。
对我来说,选项1A是可以的,但开始引入依赖地狱。你可以通过将项目A和B放在一个monorepo中来在某种程度上解决这个问题,但这是一个完全不同的对话,这让我想到...

选项二:模块联合

Module federation是Webpack 5的一项功能,允许您从远程构建导入模块。从概念上讲,项目A将拥有自己的(next.js)构建,而项目B将拥有单独的构建(就像他们现在所做的那样)。使用模块联合,您可以将Editor组件作为项目B的联合模块公开,并配置项目A以使用该模块。
在next.js中使用时,需要nextjs-mf plugin

// next.config.js for project B
/** @type {import('next').NextConfig} */
const NextFederationPlugin = require('@module-federation/nextjs-mf');

module.exports = {
    webpack(config, options) {
        ...,
        config.plugins.push(
            new NextFederationPlugin({
                name: 'ProjectB',
                filename: 'static/chunks/remoteEntry.js',
                shared: { ... },
                exposes: {
                    './Editor': './src/path/Editor.tsx',
                },
                ...
            })
        );
    }


查看软件包自述文件中的示例,了解如何配置项目A。注意,NextFederationPlugin选项的shared属性允许您指定在项目A和B之间共享哪些依赖项,这满足了您不捆绑重复依赖项的要求。请注意,react和其他一些东西默认使用此插件共享(请参阅DEFAULT_SHARE_SCOPE)。

注意事项:

  • 一个缺点是热重装不工作,对不起。
  • Nextjs-mf插件目前不支持Next.js 13的应用程序路由器(这是非常令人沮丧的IMO)。

但是,这种方法的优点在于,无论何时构建和部署项目B,项目A都会自动获取这些更改,根本不需要部署项目A!

相关问题