我目前正在使用 Vite 编写一个可重用的 UI 库,它是 ShadCN Uis 组件的简单包装。 ATM 我只在几个 NextJS 应用程序中使用该库,并且仅在页面路由器组件中使用该库时一切都按预期工作,但在应用程序路由器组件中使用它时出现导入错误。
这些错误是由库中第三方依赖项
react-hook-form
的导入(和重新导出)引起的。
详细来说,我在运行时得到以下信息npm run build
:
./node_modules/shadcn-ui-lib/dist/index6.js Attempted import error: 'FormProvider' is not exported from 'react-hook-form' (imported as 'u').
Import trace for requested module: ./node_modules/shadcn-ui-lib/dist/index6.js ./node_modules/shadcn-ui-lib/dist/index.js ./src/app/rsc/page.tsx
./node_modules/shadcn-ui-lib/dist/index6.js Attempted import error: 'Controller' is not exported from 'react-hook-form' (imported as 'p')
以下是错误中提到的消费应用程序
index6.js
中 node_modules
的一些摘录:
import { Label as f } from "./index5.js";
import { Slot as F } from "@radix-ui/react-slot";
import * as e from "react";
import { FormProvider as u, Controller as p, useFormContext as x } from "react-hook-form";
import { useForm as M } from "react-hook-form";
const R = u;
// ...
export { R as Form, C as FormControl, w as FormDescription, $ as FormField, I as FormItem, g as FormLabel, E as FormMessage, M as useForm, a as useFormField };
有一些有趣的事情我真的没有解释:
Slot
导入 @radix-ui/react-slot
正在按预期工作,即使我以相同的方式处理对 @radix-ui/react-slot
和 react-hook-form
的依赖关系(请参阅稍后的 package.json
)。node_modules
文件夹时,我可以看到 react-hook-form
已安装,并且 FormProvider
已按预期从 react-hook-form
导出。react-hook-form
导入任何内容,但一旦我使用库中的 any 组件,也会发生错误。库/vite.config.ts
// @ts-ignore Not sure how to solve this but not worth the time to figure it out...
import * as packageJson from './package.json';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts';
export default defineConfig({
plugins: [
react({
// Not really required but seems to make the bundle a bit smaller
jsxRuntime: 'classic',
}),
dts({
include: ['src/**/*'],
}),
],
build: {
lib: {
entry: resolve(__dirname, 'src', 'index.ts'),
formats: ['es'],
fileName: 'index',
},
rollupOptions: {
// Do not include the deps and peerDeps in the build.
external: [...Object.keys(packageJson.peerDependencies || {}), ...Object.keys(packageJson.dependencies)],
// `preserveModules` makes the lib tree shakable (in combination with `sideEffects: false` in `package.json`).
output: { preserveModules: true, exports: 'named' },
},
target: 'esnext',
sourcemap: true,
},
});
库/package.json
{
"name": "shadcn-ui-lib",
// ...
"sideEffects": false,
"type": "module",
"exports": {
".": "./dist/index.js",
"./styles.css": "./dist/styles.css",
"./tailwind-config": "./dist/tailwind.config.ts"
},
"module": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsc && vite build && npm run build:styles && npm run copy:files-to-dist",
"build:styles": "postcss ./src/index.css -o ./dist/styles.css && node ./build-scripts/inject-tw-directives",
"copy:files-to-dist": "copyfiles -f ./tailwind.config.ts dist",
"dev": "vite",
},
"dependencies": {
// ...
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-slot": "^1.0.2",
// ...
"react-hook-form": "^7.50.1",
// ...
},
"devDependencies": {
// ...
"typescript": "^5.2.2",
"vite": "^5.0.8",
"vite-plugin-dts": "^3.7.2"
},
"peerDependencies": {
"lucide-react": "^0.323.0",
"postcss": "^8.4.34",
"react": "18.2.0",
"tailwindcss": "^3.4.1"
},
"engines": {
"node": ">=16"
}
}
库/src/components/form.tsx 拥有来自
react-hook-form
) 的麻烦进口和再出口的来源:
'use client';
import { cn } from '../lib/css.utils.js';
import { Label } from './label.js';
import type * as LabelPrimitive from '@radix-ui/react-label';
import { Slot } from '@radix-ui/react-slot';
import * as React from 'react';
import { Controller, type ControllerProps, type FieldError, type FieldPath, type FieldValues, FormProvider, useForm, useFormContext } from 'react-hook-form';
const Form = FormProvider;
// Definitions of form components, hooks etc. (`FormItem`, `FormField`, `useFormField`, ...)
export { useFormField, Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField, useForm };
消费应用程序/src/app/rsc/page.tsx
import { Card } from 'shadcn-ui-lib';
export default function Home() {
return (
<main>
<Card>RSC test page</Card>
</main>
);
}
我明确将我的库设置为“仅 ESM”,因为尝试导出 CJS 和 ESM 会导致消费应用程序出现问题,否则我无法解决这些问题。也许这就是导致问题的原因? 在比较
@radix-ui/react-slot/package.json
(工作正常)和 react-hook-form/package.json
(导致问题)时,我发现 react-slot
不提供任何 cjs
文件,而 react-hook-form
确实提供 cjs
和 esm
文件。
我的消费应用程序(或库本身)是否可能尝试从
cjs
导入 react-hook-form
文件而不是 esm
文件?如果是这样,我该如何解决这个问题?
您可以在此处下载库的最小示例和测试使用应用程序:
可以通过在使用应用程序中运行
npm i && npm run dev
或 npm run build
来重现问题。
我对库创作相当陌生,并且仍然无法理解 ESM 和 CJS 的细节以及选择其中之一和/或另一个的所有含义。
此外,我不确定问题是否是由我的库、使用应用程序或第三方依赖项引起的。
无论如何,我真的很感激一些帮助,并且很乐意在需要时提供更多信息。
我直接在我的消费应用程序中使用
react-hook-form/package.json
进行了一些摆弄,发现了以下内容:
当从
条件导出中删除
react-server
条件时,一切都按预期工作(至少乍一看,因为我没有再遇到构建错误......):
"exports": {
"./package.json": "./package.json",
".": {
"types": "./dist/index.d.ts",
"react-server": "./dist/react-server.esm.mjs", // <-- Remove this
"import": "./dist/index.esm.mjs",
"require": "./dist/index.cjs.js"
}
},
所以看来这些错误不是由 ESM/CJS 引起的,而是由这个
react-server
导出引起的。
这显然提出了一个问题:我可以在我的消费应用程序或库中进行哪些更改才能使这项工作正常进行?
仅供参考,在 Next JS 应用程序路由器应用程序中直接导入
react-hook-form
时,这不是问题,但只有“通过”我的库导入时才会出现问题。
默认情况下,应用程序内的组件是 React Server 组件。
https://nextjs.org/docs/app/building-your-application/routing
如果您在 NextJS 13 或更高版本上运行,则默认情况下所有组件都是服务器端组件,其中 不支持钩子或 JavaScript 事件处理程序之类的东西。
https://github.com/react-hook-form/react-hook-form/issues/11307#issuecomment-1858671480