从0到1教你如何发布自己的NPM包
1. 创建一个项目
▲ 新建一个文件夹并打开终端初始化项目:
npm init
此时会根据你所填信息自动生成 package.json 的配置文件,这里主要包含了项目名称、版本号、作者、许可证等信息,同时可以记录项目的依赖信息以及自定义的脚本生成package.json文件
name
字段指定了项目的名称为bian-base64
。version
字段指定了项目的版本号为1.0.0
。description
字段提供了项目的简要描述。type
字段指定了项目模块的类型为module
,支持CommonJS和ES模块。main
字段指定了项目的主入口文件为dist/index.js
。types
字段指定了项目的类型声明文件为dist/index.d.ts
。files
字段指定了项目发布时应包含的文件夹为dist
。scripts
字段定义了项目的脚本,用于构建项目。其中build
脚本命令使用了rollup -c
来构建项目。keywords
字段是项目的关键词列表,用于搜索和分类。author
字段指定了项目的作者为bian 534893106@qq.com
。license
字段指定了项目的许可协议为ISC
。
{
"name": "bian-base64",
"version": "1.0.0",
"description": "这是一个base64工具包",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": ["dist"],
"scripts": {
"tsc": "tsup --config=tsup.config.miniprogram.ts",
"prebuild": "rimraf dist",
"build": "tsc && rollup -c rollup.config.ts",
"start": "rollup -c rollup.config.ts -w"
},
"keywords": ["base64"],
"author": "bian <534893106@qq.com>",
"license": "ISC"
}
2. 安装打包工具
Webpack
:是一个非常强大的模块打包器,它能够处理多种类型的模块(包括JavaScript、CSS、图片等),通过加载器(loaders)和插件(plugins)支持各种静态资源的处理和优化。Webpack支持代码拆分、懒加载等高级特性,适用于大型和复杂的项目构建。Rollup
:专注于ES模块的打包工具,以其小巧、高效著称。Rollup擅长将小到中型的代码库打包成高性能的、适合浏览器或Node.js使用的模块。与Webpack相比,Rollup在处理纯ES模块项目时更为轻量级和快速,特别适合库的开发。Parcel
:是一款快速、零配置的Web应用打包工具。Parcel强调开箱即用,自动处理诸如代码转换(如Babel)、样式处理(如PostCSS)、图片优化等任务,无需繁琐的配置。它的目标是提供一个简单而高效的开发体验,特别适合快速原型开发或是对配置要求不高的项目。
常见的打包工具有webpack
、rollup
、parcel
等,这里选择rollup
,因为rollup可以支持ES
模块和CommonJS
模块,并且可以支持TypeScript
,所以安装rollup
和typescript
。tsup
用于打包TypeScript代码
npm install rollup typescript rimraf tsup -D
3. 创建包的入口文件
▲ 根目录下创建 src
文件夹,并在其中创建 index.ts
文件,用于编写代码` 配置文件
class Base64Utility {
private static readonly _keyStr: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
public static encode(input: string): string {
let output = "";
let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
let i = 0;
input = Base64Utility._utf8Encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
Base64Utility._keyStr.charAt(enc1) +
Base64Utility._keyStr.charAt(enc2) +
Base64Utility._keyStr.charAt(enc3) +
Base64Utility._keyStr.charAt(enc4);
}
return output;
}
public static decode(input: string): string {
let output = "";
let chr1, chr2, chr3;
let enc1, enc2, enc3, enc4;
let i = 0;
input = input.replace(/[^A-Za-z0-9+/=]/g, "");
while (i < input.length) {
enc1 = Base64Utility._keyStr.indexOf(input.charAt(i++));
enc2 = Base64Utility._keyStr.indexOf(input.charAt(i++));
enc3 = Base64Utility._keyStr.indexOf(input.charAt(i++));
enc4 = Base64Utility._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 !== 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 !== 64) {
output = output + String.fromCharCode(chr3);
}
}
output = Base64Utility._utf8Decode(output);
console.log("开始解码-结果为",output)
return output;
}
private static _utf8Encode(input: string): string {
let output = "";
for (let n = 0; n < input.length; n++) {
let charCode = input.charCodeAt(n);
if (charCode < 128) {
output += String.fromCharCode(charCode);
} else if (charCode > 127 && charCode < 2048) {
output += String.fromCharCode((charCode >> 6) | 192);
output += String.fromCharCode((charCode & 63) | 128);
} else {
output += String.fromCharCode((charCode >> 12) | 224);
output += String.fromCharCode(((charCode >> 6) & 63) | 128);
output += String.fromCharCode((charCode & 63) | 128);
}
}
return output;
}
private static _utf8Decode(input: string): string {
let output = "";
let i = 0;
let c1, c2, c3;
while (i < input.length) {
c1 = input.charCodeAt(i++);
if (c1 < 128) {
output += String.fromCharCode(c1);
} else if (c1 > 191 && c1 < 224) {
c2 = input.charCodeAt(i++);
output += String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
} else {
c2 = input.charCodeAt(i++);
c3 = input.charCodeAt(i++);
output += String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
}
}
return output;
}
}
export default Base64Utility;
4. 配置rollup
执行以下命令,安装rollup相应配置依赖
npm install rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-json rollup-plugin-sourcemaps rollup-plugin-terser rollup-plugin-typescript2 rimraf tsup -D
rollup-plugin-node-resolve
用于解析第三方模块
rollup-plugin-commonjs
用于将CommonJS模块转换为ES模块
rollup-plugin-typescript2
用于TypeScript编译
rollup-plugin-json
用于处理JSON文件
rollup-plugin-terser
用于压缩代码
rimraf
用于删除文件
▲ 根目录下创建 rollup.config.js
配置文件
import resolve from 'rollup-plugin-node-resolve' // 用于解析第三方模块
import commonjs from 'rollup-plugin-commonjs' // 用于将CommonJS模块转换为ES模块
import typescript from 'rollup-plugin-typescript2' //用于TypeScript编译
import json from 'rollup-plugin-json' // 用于处理JSON文件
import { terser } from 'rollup-plugin-terser' // 用于压缩代码
export default {
// 指定输入文件的路径,这里是项目的入口点
input: `src/index.ts`,
output: {
// 指定输出文件的全局变量名,以便在全局范围内引用
name: 'Base64Utility',
// 指定输出文件的路径和名称
file: 'dist/index.js',
// 指定输出文件的格式,这里是UMD格式,可以在浏览器和Node.js环境中使用
format: 'umd'
},
// 使用的插件列表,这里只包含typescript插件,用于编译TypeScript代码
plugins: [
json(),
typescript({ useTsconfigDeclarationDir: true }),
commonjs(), // 将CommonJS模块转换为ES模块
resolve(), // 解析第三方模块
terser()
],
}
▲ 根目录下创建 tsup.config.miniprogram.ts
配置文件
tsup.config.miniprogram.ts
是一个配置文件,用于TSUP(TypeScript Utility Program)这一工具。TSUP是一个零配置的打包工具,特别适合于打包TypeScript库或Node.js应用程序,但它也可以通过配置文件进行更细致的控制。
import { defineConfig } from "tsup"
export default defineConfig({
entry: ["./src/index.ts"],
clean: true,
outDir: "miniprogram_dist",
dts: true,
minify: true, // 开启压缩选项
format: ["iife"],
target: "es5",
noExternal: ["@noble/curves"],
tsconfig: "tsconfig.json",
esbuildOptions(options) {
if (options.define) {
options.define.__BUILD_TS__ = Date.now().toString()
options.define.import = "require"
}
options.globalName = "Base64Utility" // 将您的方法暴露到全局作用域
options.supported = {
"dynamic-import": false,
}
},
})
5. 配置 TypeScript
▲ 根目录下创建 tsconfig.json
配置文件,添加如下配置:
{
"compilerOptions": {
"moduleResolution": "node",
"target": "es5",
"module":"es2015",
"lib": ["es2015", "es2016", "es2017", "dom"],
"strict": true,
"declaration": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"declarationDir": "dist/types",
"outDir": "lib",
"typeRoots": [
"node_modules/@types"
]
},
"include": [
"src"
]
}
moduleResolution
: 指定模块解析策略,采用Node.js风格compilerOptions
: 译器选项配置,用于指导TypeScript如何编译代码target
: 指定编译器输出的JavaScript版本为ES5。module
: 定模块系统,使用ES2015模块系统esModuleInterop
: 启用ES模块的互操作性。lib
: 指定编译器使用的库,包含ES2015、ES2016、ES2017和DOM。strict
: 启用严格的类型检查。declaration
: 生成类型声明文件(.d.ts 文件)。allowSyntheticDefaultImports
: 启用默认导入的语法。experimentalDecorators
: 启用装饰器语法。emitDecoratorMetadata
: 启用装饰器元数据的编译。declarationDir
: 指定类型声明文件的输出目录为dist/types。outDir
: 指定编译后的JavaScript代码的输出目录为lib。typeRoots
: 指定类型声明文件的根目录,这里是node_modules/@types。include
: 指定要编译的源代码文件,这里是src目录下的所有文件。
6. 构建打包
执行npm run build
此时会在根目录下生成dist目录,里面有index.js和types/index.d.ts两个文件。
7. 发布packege包到npm
- 首先需要注册一个npm账号,可以去npm官网按照步骤完成注册。
- 登录npm账号,登录前先检查一下npm源,很多人开发是已将把npm 源换成了淘宝镜像或者自己公司内部的,但是发布需要
npm
本身的源:https://registry.npmjs.org/
- 项目根路径输入
npm login
后按要求填写账号密码,然后输入npm publish
发布包。
8. 测试使用
发布完成后我们要自己测试一下是否真的将包发布到npm社区了。
首先我们可以用账号登录npm,进入到个人的信息页面,点击头像,选择package,就会进入到个人的包管理页面了。
项目中引入刚刚发布的包
npm i bian-base64
9. package后期迭代更新
- 给package添加一个readme文件,顺便测试一下怎么更新包。在文件目录下新建了一个README文件,编辑好内容保存
- 内容改好或者修改代码后不能直接发布,我们需要修改package的version号,修改之前先了解下npm维护package版本的规则x.y.z
x: 主版本号,通常有重大改变或者达到里程碑才改变;
y: 次要版本号,或二级版本号,在保证主体功能基本不变的情况下,如果适当增加了新功能可以更新此版本号;
z: 尾版本号或者补丁号,一些小范围的修修补补就可以更新补丁号.
npm version patch <=> z++
npm version minor <=> y++ && z=0
npm version major <=> x+= && y=0 && z=0
-我们当前的版本号是1.0.0,这里只是加了个readme.md文件,那就将这次版本修改为1.0.1,在包的根目录下,命令行运行以下命令:
// 更新一个补丁版本 1.0.1
npm version patch
// 更新到npm
npm publish
常用的操作package的npm命令
// 查看当前依赖的所有版本信息
npm view [包名]
// 更新当前依赖版本为最新
npm update [包名]