首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

2024该学会发布npm私有包啦

npm是什么

npm是Node Package Manager的缩写,是一个用于JavaScript包管理的工具。它是Node.js生态系统中的一部分,用于帮助开发者在项目中管理和共享代码包。npm的主要功能包括:

包管理: npm允许开发者通过命令行工具安装、升级、删除和管理JavaScript包(也称为模块或库)。这些包可以包含代码、依赖关系、配置文件等,以便在项目中共享和重用。

依赖管理: npm能够管理项目所需的依赖关系,并确保这些依赖关系的正确版本被安装。这有助于确保项目的稳定性和可重复性。

版本控制: npm使用语义版本控制(Semantic Versioning)来管理包的版本。这有助于开发者了解何时可以安全地更新其项目中的依赖项。

命令行工具: npm提供了一个命令行界面,使开发者能够方便地执行各种与包管理相关的任务,如安装、升级、搜索和发布包等。

包的发布和共享: 开发者可以使用npm将他们的JavaScript包发布到npm注册表中,这是一个用于存储和共享JavaScript包的在线仓库。其他开发者可以通过npm安装并使用这些包。

什么是打包

在软件开发中,打包(Packaging)是指将应用程序或项目的各个组成部分(例如源代码文件、依赖项、配置文件等)整合到一个或多个文件中,以便于分发、部署或交付给最终用户。前端常见的项目构建工具(如Webpack、Parcel、Rollup等),则打包通常包括构建输出物,这些文件包含了经过编译、压缩和优化的代码。在深入对比Webpack、Parcel、Rollup打包工具中,我们总结了,rollup相比于webpack更适合打包一些第三方的类库,因此本文主要通过rollup来进行打包。

什么是npm域级包

npm的域级包(Scoped Packages)是一种npm包的命名约定,它允许开发者在包的名称前添加一个命名空间,通常使用“@”符号。这个命名空间通常与组织、公司或个人的名称相关联,以确保包名称的唯一性。

域级包的命名格式如下:

@scope/package-name

其中,@scope是域名,可以是组织、公司或个人的名称,package-name是包的实际名称。例如,一个名为my-package的公司或组织example的域级包可以命名为:

@example/my-package

使用域级包的优势包括:

唯一性: 通过使用域名作为前缀,可以确保包名称的唯一性。即使不同的组织、公司或个人使用相同的包名称,由于它们的域名不同,这些包也会被区分开来。

组织和结构: 域级包有助于将包组织成逻辑上相关的集合。这对于大型项目、公司内部项目或共享包的情况非常有用。

要发布和安装域级包,可以使用npm publish和npm install命令,并在包名称前加上域名前缀。例如:

# 发布域级包

npm publish --access public

# 安装域级包

npm install @example/my-package

发布指定文件

当你使用npm publish命令发布包时,默认情况下,npm会将整个项目目录发布到npm注册表。如果你只想发布项目的特定文件或目录,可以通过.npmignore文件或.gitignore文件来定义不需要发布的文件或目录,从而实现选择性发布。

以下是一些步骤,以及如何使用.npmignore或.gitignore来限制发布的文件:

使用 .npmignore 文件

创建 .npmignore 文件: 在项目根目录下创建一个名为.npmignore的文件。

编辑 .npmignore 文件: 在.npmignore文件中列出你不想发布的文件或目录,就像编辑.gitignore文件一样。

# .npmignore

# 不发布的文件或目录

node_modules/

test/

docs/

运行 npm publish 命令: 确保.npmignore文件包含你希望排除的文件和目录后,运行npm publish命令。

使用 .gitignore 文件

如果项目中已经存在.gitignore文件,你也可以使用它来达到相同的目的。

编辑 .gitignore 文件: 在.gitignore文件中列出你不想发布的文件或目录。

# .gitignore

# 不发布的文件或目录

node_modules/

test/

docs/

在 package.json 文件中添加 "files" 字段: 如果你使用.gitignore而不是.npmignore,你还需要在package.json文件中添加一个"files"字段,以确保只发布.gitignore中列出的文件。

// package.json

{

"name": "your-package",

"version": "1.0.0",

"files": [

"dist/",

"src/",

"README.md"

],

// 其他 package.json 配置...

}

运行 npm publish 命令: 确保.gitignore文件包含你希望排除的文件和目录,然后运行npm publish命令。

npm包项目开发

创建项目

创建名为hello-npm文件夹,然后在该目录下,执行npm init -y初始化一下

/hello-npm

├─ .babelrc.json    //babel配置文件

├─ .gitignore

├─ LICENSE

├─ package-lock.json

├─ package.json

├─ README.md

├─ rollup.config.js  //rollup构建配置文件

├─ src

│  ├─ deepClone.ts

│  ├─ index.ts      //主入口文件

│  ...

└─ tsconfig.json    //ts配置文件 可以用tsc命令生成

安装依赖

//ts相关包

npm i -D typescript tslib

//rollup相关包

npm i -D rollup @rollup/plugin-node-resolve rollup-plugin-commonjs rollup-plugin-typescript

//babel相关

npm i -D @rollup/plugin-babel

npm i -D @babel/core @babel/preset-env

配置tscofig.json

全局安装typescript,用tsc命令生成tscofig.json文件,你也可以自己手动新建

npm i -g typescript

tsc --init

// tsconfig.json

{

"compilerOptions": {

"target": "es5",  // 要改成es5,不然babel转换es6的时候有些转换不了

"module": "commonjs",

"esModuleInterop": true,

"forceConsistentCasingInFileNames": true,

"strict": true,

"noImplicitAny": false,

"noImplicitThis": false,

"skipLibCheck": true

}

}

配置babel

// .babelrc.json

{

"presets": [

"@babel/env"

]

}

配置rollup

// rollup.config.js

import resolve from "@rollup/plugin-node-resolve";

import babel from "@rollup/plugin-babel";

import commonjs from "rollup-plugin-commonjs";

import typescript from "rollup-plugin-typescript";

export default {

input: "src/index.ts", // 打包入口

output: {

// 打包出口

file: "dist/index.js",

format: "umd", // umd是兼容amd/cjs/iife的通用打包格式,适合浏览器

name: "utilibs", // cdn方式引入时挂载在window上面用的就是这个名字

sourcemap: true,

},

plugins: [

// 打包插件

resolve(), // 查找和打包node_modules中的第三方模块

commonjs(), // 将 CommonJS 转换成 ES2015 模块供 Rollup 处理

typescript(), // 解析TypeScript

babel({ babelHelpers: "bundled" }), // babel配置,编译es6

],

};

实现deepClone方法

这里我们故意用es6的箭头函数来写,后面测试babel有没有把他编译成es5的语法

// src/deepClone.ts

/**

* 深拷贝

* @param obj 需要深拷贝的对象

* @returns

*/

const deepClone = (obj: Object) => {

// 不是引用类型或者是null的话直接返回

if (typeof obj !== "object" || typeof obj == null) {

return obj;

}

// 初始化结果

let result: object;

if (obj instanceof Array) {

result = [];

} else {

result = {};

}

for (let key in obj) {

// 保证不是原型上的属性

if (obj.hasOwnProperty(key)) {

// 递归调用

result[key] = deepClone(obj[key]);

}

}

return result;

};

export default deepClone;

在主入口文件处引入刚刚写的深拷贝

// src/index.ts

import deepClone from "./deepClone";

export {

deepClone

};

配置package.json

增加一个main属性,到时候打包发布到npm以后在项目里import引入时的主入口文件,和rollup的打包出口文件相对应

增加script打包命令

{

"name": "ldp-hello-npm",

"version": "1.0.0",

"description": "js通用方法库",

"main": "index.js",    //主入口文件

"type": "module",

"scripts": {

"build": "rollup --config",

"test": "echo \"Error: no test specified\" && exit 1"

},

"keywords": [

"toolkit",

"rollup",

"typescript"

],

"author": "lh",

"license": "ISC",

"devDependencies": {

"@babel/core": "^7.23.7",

"@babel/preset-env": "^7.23.7",

"@rollup/plugin-babel": "^6.0.4",

"@rollup/plugin-node-resolve": "^15.2.3",

"rollup": "^4.9.2",

"rollup-plugin-commonjs": "^10.1.0",

"rollup-plugin-typescript": "^1.0.1",

"tslib": "^2.6.2",

"typescript": "^5.3.3"

}

}

打包编译

npm run build

可以看到打包成功了

看看dist目录下的产物

可以看到确实生成了一个index.js对应我们rollup配置的打包出口文件,这也是我们package.json中配置的main属性到时候导入要用到的入口文件

刚才故意用箭头函数写的深拷贝也成功被转成了es5的语法

本地调试模拟npm包安装导入验证

准备一个正常的项目,我们这里使用vite工具搭建一个简易的名为vite-project项目,如要搭建完善的项目脚手架请看我之前发的公众号

npm create vue@latest

cd 

npm install

npm run dev

把我们hello-npm中的package.josn和README.md拷贝到dist目录下

用npm link链接到全局

cd dist

npm link

提示成功,可以去自己全局安装的npm的node_modules下找一找是不是有

接下里去vite-project项目里link这个包, 这里注意要带上包名(package.json里面配置的name)

npm link ldp-hello-npm

取消link

npm unlink ldp-hello-npm

这里用npm link引入以后有eslint的报错的话加个.eslintignore文件忽略链接到的这个地址的eslint检查

在vite-project的main.ts里面引入写上这样一段代码测试一下有没有生效

// src/main.ts

import { deepClone } from "ldp-hello-npm";

const obj: any = { a: 100, b: { c: 200 } };

const objCopy = deepClone(obj);

obj.b.c = 300;

console.log("obj:", obj);

console.log("objCopy", objCopy);

这里import引入utilibs时没有代码补全提示,并且启动有找不到模块的报错

我们回到hello-npm项目中:

tsconfig.json配置修改

{

"compilerOptions": {

"target": "es5",  // 要改成es5,不然babel转换es6的时候有些转换不了

"module": "commonjs",

"declaration": true, // 根据ts文件自动生成.d.ts声明文件和js文件

"emitDeclarationOnly": true, // 只输出.d.ts声明文件,不生成js文件

"outDir": "./dist", // 输出目录

"esModuleInterop": true,

"forceConsistentCasingInFileNames": true,

"strict": true,

"noImplicitAny": false,

"noImplicitThis": false,

"skipLibCheck": true

}

}

修改package.json,增加scripts脚本和types属性

"types": "index.d.ts",  // 声明文件的主入口

"scripts": {

"build:types": "tsc",

"build": "npm run build:types && rollup --config",

"test": "echo \"Error: no test specified\" && exit 1"

},

重新去执行npm run build,就可以生成声明文件了

切回vite-project项目看看,就有引入提示了

如果以前改过npm的镜像地址,比如使用了淘宝的镜像,就先改回来

npm config set registry https://registry.npmjs.org/

本地验证结束,自然是要发布到npm上去了,先去npm官网上面注册一个npm的账号

注意dist目录下要有package.json和README.md, npm官网上显示的信息都是这里面读取;

package.json里的version每次发布都要比之前的大

cd dist

npm addUser or npm login // 添加账户输入账号密码邮箱和邮箱验证码

打开链接验证(由于我添加过之后再次执行上边命令就会验证账号)

如果不知道当前登录的账号可以用who命令查看身份:

npm who am i

npm profile get // 查看绑定的账号信息

还可以用下面命令退出当前账号

npm logout

登录成功就可以将我们的包推送到npm上去了

npm publish

发布npm包时可能会遇到的一些坑

邮箱未验证

npm ERR! publish Failed PUT 403

npm ERR! code E403

npm ERR! you must verify your email before publishing a new package: https://www.npmjs.com/email-edit : your-package

没有权限发布

npm ERR! publish Failed PUT 403

npm ERR! code E403

npm ERR! You do not have permission to publish "your-package". Are you logged in as the correct user? : your-package

包和别人的包重名了。修改包名3. 源设置成第三方源,比如设置了淘宝镜像。只要把源设为默认的即可

npm包开发模板源码:https://github.com/LHNB521/hello-npm.git

  • 发表于:
  • 原文链接https://page.om.qq.com/page/O9nAKtDPpYMdx9cHF1QBKvwg0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券