前言
在之前实现了一个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.json
中 compilerOptions
字段加入此配置后解决: "moduleResolution": "nodenext"
找不到模块"../package.json"。请考虑使用 "--resolveJsonModule" 导入带 ".json" 扩展的模块
在.ts文件中引入json文件,vscode报错:
解决: tsconfig.json
中 compilerOptions
字段加入如下配置后解决:
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?
然后根据提示安装 tslib
:pnpm 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
require
是 CommonJS
规范中用于导入模块的关键字,而浏览器环境下是不认识 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')