Next.js发布npm组件库导入时提示无对应loader处理文件
使用NextJS构建组件库时,本地运行状态正常,但发布到npm作为模块导入时触发以下报错:
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
报错截图:
尝试使用next-transpile-modules作为临时方案时出现两种互斥的配置结果:
- 配置1:发布后的npm包可在其他项目正常导入访问,但本地执行
build或dev命令启动组件预览时触发上述loader报错 - 配置2:本地构建、启动可正常运行,但发布后的npm包无法正常使用
两种存在问题的配置
配置1:npm包可用但本地构建失败
// next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, swcMinify: true }; module.exports = nextConfig;
配置2:npm包不可用但本地构建成功
// next.config.js /** @type {import('next').NextConfig} */ const withTM = require('next-transpile-modules')([ '@ellipsis-org/component-library' ]); // 传入需要转译的模块 const nextConfig = { reactStrictMode: true, swcMinify: true }; module.exports = withTM({ nextConfig });
测试用的HelloWorld组件代码如下:
import { styled, Typography } from '@mui/material'; import React from 'react'; const HelloWorld = () => { return ( <Wrapper> <Typography variant="caption">HelloWorld</Typography> </Wrapper> ); }; export default HelloWorld; const Wrapper = styled('div')` border: 1px solid red; `;
NextJS的定位是全栈应用开发框架,并非组件库专用打包工具。直接发布NextJS项目的未编译源码到npm时,源码中包含的JSX、MUI styled模板字符串等非标准JavaScript语法不会被预编译为通用JS,而下游项目默认不会转译node_modules目录下的文件,因此触发loader缺失报错。next-transpile-modules的作用是让NextJS应用转译指定的node_modules依赖,属于使用组件库的下游NextJS项目的配置项,并非给组件库本身打包使用,将其配置在组件库项目中,自然会出现本地运行和发布后效果互斥的问题。
不要直接用NextJS的构建产物发npm包,将本地预览和组件库打包流程拆分:NextJS仅用来做本地组件预览调试,用专用打包工具输出可发布的编译后产物。
安装依赖并配置peer依赖
推荐用tsup(基于esbuild,配置简单打包速度快)作为组件库打包工具,执行安装命令:npm install tsup --save-dev在
package.json中将react、MUI等公共依赖配置为peerDependencies,避免重复打包导致版本冲突:{ "peerDependencies": { "react": ">=17.0.0", "react-dom": ">=17.0.0", "@mui/material": ">=5.0.0" } }新增组件库打包配置
在项目根目录新建tsup.config.ts:import { defineConfig } from 'tsup'; export default defineConfig({ entry: ['src/index.ts'], // 替换为组件库的统一导出入口,需在该文件中导出所有对外提供的组件 format: ['esm', 'cjs'], // 同时输出ESM和CommonJS两种模块格式,适配不同项目的导入规则 dts: true, // 自动生成TS类型声明文件 clean: true, external: ['react', 'react-dom', '@mui/material'], // 不打包peer依赖 target: 'es2020', sourcemap: true });更新
package.json中的产物入口、文件白名单和脚本命令:{ "main": "./dist/index.cjs", "module": "./dist/index.js", "types": "./dist/index.d.ts", "files": ["dist"], "scripts": { "dev": "next dev", "build": "next build", "build:lib": "tsup", "prepublishOnly": "npm run build:lib" // 发布npm前自动执行组件打包,避免漏编译 } }还原NextJS配置
组件库项目本身不需要引入next-transpile-modules,直接使用基础配置即可,本地预览走NextJS原有逻辑,发布npm走tsup打包流程,两个流程完全隔离不会冲突:// next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, swcMinify: true }; module.exports = nextConfig;下游使用说明
打包完成后发布的npm包是标准JS产物,下游无论是NextJS、Vite还是Webpack项目,都不需要额外配置转译规则,可以直接导入使用。
内容的提问来源于stack exchange,提问作者kevin

