用ts重构基于rollup的npm包踩坑记录

前言

在之前实现了一个npm包 我的第一个npm包:plugin-zip-pack - 掘金 (juejin.cn)

但源码是js编写的,最近想的使用ts进行重构一下。 最开始直接将源码中原来文件都修改为 .ts后缀后遇到了一系列报错问题、以及打包后出现的报错问题,经过一步步踩坑,最终都成功解决。谨以此篇记录自己的学习的过程,也希望能帮助到其他同学。

npm包地址: plugin-zip-pack - npm (npmjs.com)

文档地址: silin001.github.io/docs/blogs/...

一、ts文件在vscode编辑器中报错问题

编辑器提示找不到 require

根据提示,安装 @types/node 可解决:pnpm i @types/node -D

.ts文件中提示无法重新声明块范围变量

由于之前js版本都是使用的CommonJS 语法, 在 util/index.ts 中声明一个变量使用 module.exports 导出变量:

js 复制代码
const sucess = chalk.green;
module.exports = {
  sucess,
// ...
}

index.ts 中 使用require 引入

js 复制代码
const { sucess } = require('./src/util/index')
// ...

原因: 因为直接修改了文件为 .ts , 当在 TypeScript 中遇到变量重新声明的问题时,通常是因为在不同的文件中重复定义了同一个变量名。

解决: 需要使用 TypeScript 的模块系统,也就是 import、export 的方式来导入导出ts文件中的模块,并避免在不同的文件中重复声明相同的变量名。使用 es6 规范导入导出后问题解决。

Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?

引入第三方依赖时报错:

根据提示在 tsconfig.jsoncompilerOptions 字段加入此配置后解决: "moduleResolution": "nodenext"

找不到模块"../package.json"。请考虑使用 "--resolveJsonModule" 导入带 ".json" 扩展的模块

在.ts文件中引入json文件,vscode报错:

解决: tsconfig.jsoncompilerOptions 字段加入如下配置后解决:

js 复制代码
"esModuleInterop": true,
"resolveJsonModule": true

二、rollup 打包遇到的报错

Error: Cannot find module 'typescript'

ps: 找不到模块"typescript"

报错原因: 源码工程中没有安装 ts

解决: 安装ts: pnpm i typescript -D

安装完ts还是不行,打包发现提示:

(plugin typescript) Error: @rollup/plugin-typescript: Could not find module 'tslib', which is required by this plugin. Is it installed?

然后根据提示安装 tslibpnpm i tslib -D

重新使用 rollup 打包成功解决:

Uncaught ReferenceError: require is not defined

Rollup 打包代码时,在代码中使用了CommonJS语法(如:使用了require),但 Rollup 默认使用ES6模块语法(如:import、export

报错原因:

而我这里是因为我的源码中用 require 引入了node环境的一些api,如:fs等。 然后用 Rollup工具打包后生成.js 在浏览器环境(html中script引入)中使用时,此时浏览器就会报这个错: require is not defined

requireCommonJS 规范中用于导入模块的关键字,而浏览器环境下是不认识 require的!

解决方案:

  • 在代码中使用ES6模块语法替换CommonJS语法

  • 在Rollup的配置文件中使用插件解决(例如 @rollup/plugin-commonjs),以便 Rollup 能够识别并正确处理CommonJS语法。

SyntaxError: Cannot use import statement outside a module

ps: SyntaxError:不能在模块外使用 import 语句

报错原因: 在js文件中是无法直接使用 import/export es6语法

解决方案:

  • package.json 中设置type字段 "type":"module"

  • 使用 .mjs 的扩展名(从 Node.js v13.2 版本开始,Node.js 已经默认打开了 ES6 模块支持。Node.js 要求 ES6 模块采用 .mjs 的后缀文件名)

Error [ERR_REQUIRE_ESM]: require() of ES Module

ps: 试图在 index.umd.js 文件中使用了 require() 引入了一个 ES 模块(ES Module),但在这种情况下是不被支持的;

要解决这个问题,你需要调整 xxx/index.umd.js 文件中的代码。

报错原因:

错误 [ERR_REQUIRE_ESM] 表示: require() 函数试图引入一个 ES 模块,但在这个文件的上下文环境中不被支持。

对应到我的这个npm包的源码中是因为我使用 require 引入了 chalk 依赖

解决:require 修改为 import 方式引入后解决

chalk 依赖库

查看 chalk 源码发现是使用 cjs 规范导出的。

Rollup 打包后的项目中,即使 chalk 包只导出了 CommonJS 规范(CJS),仍然需要使用动态 import 引入的主要原因是为了确保代码的兼容性和可靠性

Error: Cannot find module '../package.json'

源码中使用 require 引入package.json 文件后打包后报错

报错原因: 默认情况下rollup.js 不支持导入 json 模块, 无论是 require 还是 import 方式

解决方案:

  • 安装@rollup/plugin-json插件支持使用 import 导入json文件 pnpm i -D @rollup/plugin-json

  • 使用 node的 fs.readFileSync 读取文件

使用node模块的 resolve 获取真实的json文件的路径,然后使用fs模块的readFileSync方法读取文件内容,从而获取到json中name、version等内容。

js 复制代码
const { name, version } = getPackJson()
function getPackJson () {
  const pkg = {
    name: "",
    version: "",
  };
  // 使用 resolve 获取真实的json文件 path路径
  const packageJsonPath = resolve(__dirname, "package.json");
  try {
    // 读取文件内容
    const packageJsonString = fs.readFileSync(packageJsonPath, "utf8");
    const { name, version } = JSON.parse(packageJsonString);
    pkg.name = name;
    pkg.version = version;
  } catch (err) {
    console.error("无法读取npm包的 package.json 文件:", err);
  }
  console.log("pkg--", pkg);
  return pkg;
}

三、其他

rollup打包ts类型文件

使用pnpm i -D rollup-plugin-dts插件来生成ts类型文件

js 复制代码
import { dts } from "rollup-plugin-dts";

const config = [
  // ...
  {
    input: "./src/index.ts",
    output: [{ file: "build/index.d.ts", format: "es" }],
    plugins: [dts()],
  },
];
export default config;

配置完成后 当执行 rollup -c 时会根据配置文件自动打包到 build/index.d.ts

此时,使用es语法导入时就有了智能提示:

当错误使用导出的方法时也会有ts类型提示:

重构前 npm插件在vite中使用警告

使用ts编写源码,给导出的 vite函数 添加一个 type 类型来约束:

js 复制代码
/** 支持vite插件类型 */
export type VitePluginZipPackType = {
  name: string;
  apply: "build";
  closeBundle: () => void;
};

四、npm包发布相关

npm 发布时使用 opt码进行二次验证

由于我设置了npm二次验证,发布时需要输入在手机 Authenticator App 上输入 opt 验证码即可:

发布成功会有邮件通知:

npm login报错

原因: 使用了代理软件网络环境有问题、设置的proxy代理环境有问题,可能会有缓存

解决: 关闭代理软件后重新login尝试(可能需要多次尝试)、清除npm缓存

npm unpublish 删除已发布包的指定版本

npm login 登录后直接删除指定版本: npm unpublish 包名称@版本号

注意:

  • 不允许撤销发布已经超过 24 小时的包,撤销发布 24 小时内的包需要加 --force 参数
  • 撤销之前发布的包,再次发布的时候不能与之前被撤销的包的名称/版本其中之一相同,因为这两者构成的唯一性已经被占用,官方并没有随着撤销而删除

npm deprecate 弃用发布的包

npm unpublish 官方推荐的替代命令 npm deprecate

npm deprecate 包名称@版本号

npm deprecate 命令不会在 npm官网上撤销对应的包,而是会在使用安装该包时给出下面的提示信息:

If you no longer wish to maintain a package, or if you would like to encourage users to update to a new or different version, you can deprecate it. Deprecating a package or version will print a message to the terminal when a user installs it.

npm 发布新版本后使用时版本号不更新

npm包发布新的版本号后,在项目里直接 pnpm install 进行安装时可能暂时还是旧版本号,但是npm官网已经显示有了新版本号,这是因为刚发布的包可能还没更新好,需要等待一段时间(我是当天发布了新版本npm包,次日安装时版本号才自动更新最新的)重新安装查看

五、最终npm包使用

webpack.config.js

在.js配置文件中使用 es模块 import 时会报错: SyntaxError: Cannot use import statement outside a module

所以需要使用cjs模块导入: const { test, pluginZipPackVite } = require('plugin-zip-pack')

vite.config.ts

在.ts配置文件中可以使用时 es、cjs模块引入都支持:

import { test, pluginZipPackVite } from 'test-plugin-zip-pack'

or
const { test, pluginZipPackVite } = require('plugin-zip-pack')

相关推荐
ekskef_sef19 分钟前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
高山我梦口香糖43 分钟前
[react 3种方法] 获取ant组件ref用ts如何定义?
typescript·react
sunshine64144 分钟前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻1 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云1 小时前
npm淘宝镜像
前端·npm·node.js
dz88i81 小时前
修改npm镜像源
前端·npm·node.js
Jiaberrr1 小时前
解锁 GitBook 的奥秘:从入门到精通之旅
前端·gitbook
顾平安2 小时前
Promise/A+ 规范 - 中文版本
前端
聚名网2 小时前
域名和服务器是什么?域名和服务器是什么关系?
服务器·前端
桃园码工3 小时前
4-Gin HTML 模板渲染 --[Gin 框架入门精讲与实战案例]
前端·html·gin·模板渲染