从 Vite 库(ShadCN UI 的包装库)中的第 3 方依赖项(`react-hook-form`)导入错误

问题描述 投票:0回答:1

我目前正在使用 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
时,这不是问题,但只有“通过”我的库导入时才会出现问题。

vite es6-modules react-hook-form shadcnui cjs
1个回答
0
投票

默认情况下,应用程序内的组件是 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

© www.soinside.com 2019 - 2024. All rights reserved.