文章已同步至掘金:https://juejin.cn/post/6950158304387530783
欢迎访问😃,有任何问题都可留言评论哦~
以前 JavaScript 只能运行在浏览器端,
有了 Nodejs 环境,JavaScript 也可以在后端运行了。
NPM 是用于 Nodejs 的 JavaScript 包管理器,
提供了大量的轮子,让老司机程序员瞬间提速起飞。
本例中做了一个使用JavaScript实现数据结构的包
npm install data-struct-js
- 前提 {#0.-%E5%89%8D%E6%8F%90}
前提是安装了 Nodejs,NPM 会随 Nodejs 一起安装。
如果没有 Node 环境,应该也不会有 NPM 包开发的需求了。
- NPM用户注册 {#1.-npm%E7%94%A8%E6%88%B7%E6%B3%A8%E5%86%8C}
既然是 NPM 包,首先自然是要有 NPM 的账户。
注册很简单,去官网。
另:学习的话,这里的中文文档网站不错:
记住用户名和密码,在发布时要用到。
- 新建仓库并初始化一个 NPM 项目 {#2.-%E6%96%B0%E5%BB%BA%E4%BB%93%E5%BA%93%E5%B9%B6%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%80%E4%B8%AA-npm-%E9%A1%B9%E7%9B%AE}
2.1 Github 或 Gitee 上新建仓库 {#2.1-github-%E6%88%96-gitee-%E4%B8%8A%E6%96%B0%E5%BB%BA%E4%BB%93%E5%BA%93}
这个项目就叫 data-struct-js
了。
克隆到本地。
git clone https://github.com/CoderMonkie/data-struct-js.git
2.2 初始化 NPM 项目 {#2.2-%E5%88%9D%E5%A7%8B%E5%8C%96-npm-%E9%A1%B9%E7%9B%AE}
VSCode 打开 data-struct-js 文件夹,终端中执行
npm init -y
不加 -y 的话,需要一步步的确认或手动输入
加上 -y 的参数就会全部以默认生成
上面这个命令自动为我们生成了 package.json 文件。
{
"name": "data-struct-js",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/CoderMonkie/data-struct-js.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/CoderMonkie/data-struct-js/issues"
},
"homepage": "https://github.com/CoderMonkie/data-struct-js#readme"
}
各项目根据实际做适当修改。
其中的 url
地址,
是根据我们 github 项目的 git 信息来生成的。
git 信息存在于 .git
文件夹中。
各属性我们在修改时再做具体说明。
- 准备开始 NPM 包的开发 {#3.-%E5%87%86%E5%A4%87%E5%BC%80%E5%A7%8B-npm-%E5%8C%85%E7%9A%84%E5%BC%80%E5%8F%91}
3.1 编辑 package.json 文件 {#3.1-%E7%BC%96%E8%BE%91-package.json-%E6%96%87%E4%BB%B6}
这里说明一下相关属性项目,
- name:即项目名
- version:即包的版本
- description:描述信息
- main:指定入口文件
- scripts:脚本命令
- repository:源码仓库地址
- keywords:关键词
- author:作者信息
- license:版权类型
- bugs:bug反馈
- homepage:项目主页
其中,
必须包含的项目有:name
和 version
作者信息格式官方建议
Your Name <email@example.com> (http://example.com)
版权类型,本项目中改为了限制最小的 MIT 类型。
本例中修改完成后的 package.json 文件:
{
"name": "data-struct-js",
"version": "0.0.1",
"description": "Provide commonly used data-structures for javascript.",
"main": "index.js",
"scripts": {
"compile": "npx babel src --out-dir compiled"
},
"repository": {
"type": "git",
"url": "git+https://github.com/CoderMonkie/data-struct-js.git"
},
"keywords": [
"data-structure",
"javascript"
],
"author": "Mao NianYou <maonianyou@gmail.com> (http://github.com/codermonkie)",
"license": "MIT",
"bugs": {
"url": "https://github.com/CoderMonkie/data-struct-js/issues"
},
"homepage": "https://github.com/CoderMonkie/data-struct-js#readme"
}
除了上面列出的以外,
还会有 dependencies
跟 devDependencies
,
分别表示项目的依赖包以及项目开发中用到的依赖包。
这里不写出来,是因为,当我们要添加依赖时,
通过 npm install --save
或 npm install --save-dev
来安装,
所依赖的包及版本信息都会添加到上面对应的属性中,
基本无需手动编辑。
3.2 规划项目目录和文件 {#3.2-%E8%A7%84%E5%88%92%E9%A1%B9%E7%9B%AE%E7%9B%AE%E5%BD%95%E5%92%8C%E6%96%87%E4%BB%B6}
可以比这个更复杂,
本例中最基本的结构包括以下:
data-struct-js
|--examples
| |--(for sample files)
|--lib
| |--(for compiled files)
|--src
| |--(for development files)
|--.babelrc
|--.gitignore
|--.npmignore
|--index.js
|--LICENSE
|--package.json
|--README.md
*注:上面有子层次的表示文件夹,其它为文件
3.2.1 目录结构简介 {#3.2.1-%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84%E7%AE%80%E4%BB%8B}
- data-struct-js:本项目根目录
- examples:存放本项目的示例用文件
- lib:存放编译结果(鉴于浏览器兼容性,ES6标准的JavaScript编译为ES5)
- src:开发目录
- .babelrc:babel的配置文件(使用webpack的话也可以写在 webpack.config 中)
- .gitignore:git 配置文件,配置忽略版本管理的对象
- .npmignore:npm配置文件,配置发布到 npm 时的忽略对象
- index.js:统一导出的出口文件
- LICENSE:版权信息文件,在新建仓库时根据选择自动生成
- package.json:项目管理文件,项目初始化时自动生成
- README.md:项目说明文件,可在新建仓库时生成该文件,根据项目内容自行编辑
- 另外,如果使用 webpack 的话,还要有一个 webpack.config 的配置文件
*注:本文项目中将 webpack 配置在了 examples 下的样例工程中
3.2.2 特殊文件说明 {#3.2.2-%E7%89%B9%E6%AE%8A%E6%96%87%E4%BB%B6%E8%AF%B4%E6%98%8E}
.babelrc
babel 让我们可以使用最新 JavaScript 语法,
而不用担心浏览器兼容问题,
因为它可以帮我们把 ES6 的代码编译为 ES5。
示例:
{
"presets": [
"@babel/preset-env"
]
}
.gitignore
在此配置 git 版本管理要忽略的文件或文件夹。
新建仓库时根据选择可以生成默认的配置。
自己编辑的话,至少应该有以下:
examples/dist/
lib/
node_modules/
.npmignore
.npmignore
非 npm 发布对象配置。
示例:
examples/
node_modules/
src/
.babelrc
.gitignore
.npmignore
LICENSE
在新建仓库时根据选择的版权类型会自动生成该文件。
示例:
MIT License
Copyright (c) 2019 Mao NianYou
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
`THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
`
package.json
项目配置管理文件,参照上文 3.1 中已作的说明。
使用 markdown 标记的项目说明文档,
介绍自己项目情况。比如:
# 背景介绍
# 项目介绍
# 查看示例
# 使用说明
# License
# 联系
# 贡献者/鸣谢
根据以上大项目填写具体内容
- 开发我们自己的 NPM 包 {#4.-%E5%BC%80%E5%8F%91%E6%88%91%E4%BB%AC%E8%87%AA%E5%B7%B1%E7%9A%84-npm-%E5%8C%85}
配置完上面的内容,到了主要环节,
开发我们的 NPM 包。
4.1 安装相关依赖 {#4.1-%E5%AE%89%E8%A3%85%E7%9B%B8%E5%85%B3%E4%BE%9D%E8%B5%96}
在本次开发中,我们当前仅用到了 babel 相关的包。
在 VSCode 终端(或其它命令行工具)执行以下命令:
npm install --save-dev @babel/cli
npm install --save-dev @babel/core
npm install --save-dev @babel/preset-env
也可以简写为以下一条命令:
npm i -D @babel/cli @babel/core @babel/preset-env
--save-dev 等同于 -D
会添加依赖到 devDependencies 中
--save 等同于 -S
会添加依赖到 dependencies 中 因为代码中用到了类属性注,仅安装以上的话,运行 babel 会报错,
根据错误信息,还需安装 @babel/plugin-proposal-class-properties
注:参见下文代码 ArrayBasedStruct.js
Error: Cannot find module '@babel/plugin-proposal-class-properties' from...
- 安装
npm i -D @babel/plugin-proposal-class-properties
- 修改 babel 配置文件(.babelrc),添加 plugins
{
"presets": [
"@babel/preset-env"
],
"plugins": [
["@babel/plugin-proposal-class-properties",
{
"loose": true
}
]
]
}
安装完成后,package.json 文件中就增加了 devDependencies 字段。
{
"name": "data-struct-js",
"version": "0.0.1",
"description": "Provide commonly used data-structures for javascript.",
"main": "index.js",
"scripts": {
"compile": "npx babel src --out-dir compiled"
},
"repository": {
"type": "git",
"url": "git+https://github.com/CoderMonkie/data-struct-js.git"
},
"keywords": [
"data-structure",
"javascript"
],
"author": "CoderMonkey <maonianyou@gmail.com> (http://github.com/codermonkie)",
"license": "MIT",
"bugs": {
"url": "https://github.com/CoderMonkie/data-struct-js/issues"
},
"homepage": "https://github.com/CoderMonkie/data-struct-js#readme",
"devDependencies": {
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/preset-env": "^7.7.6"
}
}
4.2 ES6 代码实现(示例:Stack.js) {#4.2-es6-%E4%BB%A3%E7%A0%81%E5%AE%9E%E7%8E%B0%EF%BC%88%E7%A4%BA%E4%BE%8B%EF%BC%9Astack.js%EF%BC%89}
因为本文主题是讲如何从零发布一个自己的 npm 包,
代码部分仅举一例说明,完整代码可查看:
[Github](github.com/CoderMonkie...)
以下为本项目中栈结构的实现代码,
其中引用到的其它文件,
一并贴在下方以供参考。
data-struct-js/src/Stack.js
import {
deepCopy,
isFunction
} from './common_utils'
import ArrayBasedStruct from './ArrayBasedStruct'
/\*\*
\*栈结构
\*
`
`
*
@export
*
@class Stack
* `
`@extends {ArrayBasedStruct}
*/
export class Stack extends ArrayBasedStruct {
constructor() {
super()
}
`
`
/**
*将新元素入栈
*
`
`
* @param {*} element
* @memberof Stack
*/
push(element) {
return this.__items.push(element)
}
`
`
/**
*栈顶元素出栈
*
`
`
* @returns 栈顶元素
* @memberof Stack
*/
pop() {
return this.__items.pop()
}
`
`
/**
*查看栈顶元素
*
`
`
* @returns 栈顶元素
* @memberof Stack
*/
peek() {
if (!this.__items.length) return undefined
return deepCopy(this.__items[this.__items.length - 1])
}
`
`
/**
*遍历栈结构
*
`
`
*
@param {function} callback
*
@param {boolean} [reversal=false]
*
@memberof Stack
*/
traverse(callback, reversal = false) {
// 检查回调函数
if (!isFunction(callback)) return
var items = this.getItems(this.__items)
var from = reversal ? items.length - 1 : 0
var to = reversal ? 0 : items.length - 1
// 循环条件
var loopCondition = function (current) {
if (reversal) {
return current >= to
} else {
return current <= to
}
}
// 游标前进
var stepIn = function (current) {
if (reversal) {
return current - 1
} else {
return current + 1
}
}
// 进行遍历
for (var index = from; loopCondition(index); index = stepIn(index)) {
var element = items[index];
callback(element, index)
}
}
`
`
/**
*转为字符串
*
`
``
`
* @returns
* `@memberof Stack
*/
toString() {
return this.__items.map(element => element.toString()).join(' ')
}
}
`
data-struct-js/src/common_utils.js
自己实现的常用方法
/**
*深拷贝
*
* @export
* @param {*} source 要拷贝的对象
* @returns 深拷贝结果
*/
export function deepCopy(source) {
var dest
if(Array.isArray(source)) {
dest = []
for (let i = 0; i < source.length; i++) {
dest[i] =deepCopy(source[i])
}
}
else if(toString.call(source) === '[object Object]') {
dest = {}
for(var p in source){
if(source.hasOwnProperty(p)){
dest[p]=deepCopy(source[p])
}
}
}
else {
dest = source
}
return dest
}
/\*\*
\*判断传入参数是否为函数
\*
`
`
* @export
* @param {*} func 参数(函数)
* `@returns true:是函数 false:不是函数
*/
export function isFunction (func) {
if (!func || toString.call(func) !== '[object Function]') return false
return true
}
`
data-struct-js/src/ArrayBasedStruct.js
给栈结构提供的基类
import { deepCopy } from "./common_utils"
/\*\*
\*基于数组实现的数据结构的基类
\*
`
`
* `
`@class ArrayBasedStruct
*/
export class ArrayBasedStruct {
constructor() {
this.__items = []
}
`
`
/**
*获取所有元素
*
`
`
* @returns 元素集合
* @memberof Stack
*/
getItems () {
return deepCopy(this.__items)
}
`
`
/**
*数据结构实例中是否包含元素
*
`
`
* @readonly
* @memberof ArrayBasedStruct
*/
get isEmpty() {
return this.__items.length === 0
}
`
`
/**
*数据结构实例的元素个数
*
`
`
* @readonly
* @memberof ArrayBasedStruct
*/
get size() {
return this.__items.length
}
`
`
/**
*清空数据结构中的元素
*
`
``
`
* `@memberof ArrayBasedStruct
*/
clear() {
this.__items.length = 0
}
}
`
通过 index.js 文件统一导出
data-struct-js/index.js
import { Stack } from './lib/Stack'
export { Stack }
注:目前本示例中只有 Stack,后续添加的也都是在这里统一导出
4.3 使用 babel 进行编译 {#4.3-%E4%BD%BF%E7%94%A8-babel-%E8%BF%9B%E8%A1%8C%E7%BC%96%E8%AF%91}
还记得我们在 package.json 中配置的脚本命令吗,
"scripts": {
"compile": "npx babel src/ --out-dir lib"
},
执行方法:
npm run compile
执行结果:
> data-struct-js@0.0.1 compile F:\path\to\data-struct-js
> npx babel src/ --out-dir lib
`Successfully compiled 3 files with Babel.
`
编译执行成功,可以看到 data-struct-js/lib/
文件夹下生成了编译后的文件。
我们做的包在被别人使用时,下载和引入的就是这些编译后的文件。
- 测试自己的 NPM 包 {#5.-%E6%B5%8B%E8%AF%95%E8%87%AA%E5%B7%B1%E7%9A%84-npm-%E5%8C%85}
跟普通项目开发一样 NPM 包开发也需要测试。
通过测试,减少或避免产生 bug。
5.1 创建 demo 项目 {#5.1-%E5%88%9B%E5%BB%BA-demo-%E9%A1%B9%E7%9B%AE}
data-struct-js/examples/
路径下,新建我们的测试用项目。
cd examples
npm init
package name: data_struct_js_demo
生成的 package.json 项目文件:
{
"name": "data_struct_js_demo",
"version": "1.0.0",
"description": "demo for data-struct-js",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT"
}
告诉你个小秘密:
这个工程位于我们主工程的目录底下,
这里要用到的依赖包,如果本工程没有安装,
则会自动查找上层目录,
也就是,外层安装了的话就可用。
另外,
找到根目录也没有的话还会查看全局安装,
都没有的时候才会报错
5.2 完成相关配置 {#5.2-%E5%AE%8C%E6%88%90%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE}
5.2.0 安装 data-struct-js {#5.2.0-%E5%AE%89%E8%A3%85-data-struct-js}
注意:
以下安装方式,不适用于本次示例,
因为 demo 工程位于 package 工程目录下,
会导致递归引用。
仅当不在同一路径下创建新工程时可以。
npm install --save-dev ../../data-struct-js
所以,这里我们需要手动引用我们包里的文件。
data-struct-js/examples/src/index.js
// 直接引入原文件
import Stack from '../../src/Stack'
`// 或编译后的也可以
// import Stack from '../../lib/Stack'
`
注:
这里有个坑,就是模块化方式必须统一!
也就是,如果代码里用 ES6 的模块化方式(import/export),
那么这里如果用 Node 的模块化(require/module.exports=),
就会报错。(new 导出的class的时候报:xxx is not a constructor)
=>
data-struct-js/index.js
也是这样
5.2.1 安装其它所需依赖包 {#5.2.1-%E5%AE%89%E8%A3%85%E5%85%B6%E5%AE%83%E6%89%80%E9%9C%80%E4%BE%9D%E8%B5%96%E5%8C%85}
这里列出我们本次用到的开发依赖:
(为了避免太长,分两行写)
npm i -D webpack webpack-cli webpack-dev-server
npm i -D html-webpack-plugin clean-webpack-plugin
data-struct-js/examples/package.json
{
"name": "data_struct_js_demo",
"version": "1.0.0",
"description": "Demo for data-struct-js",
"main": "index.js",
"scripts": {
},
"author": "CoderMonkey <maonianyou@foxmail.com>",
"license": "MIT",
"devDependencies": {
"babel-loader": "^8.0.6",
"clean-webpack-plugin": "^3.0.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.41.3",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
}
}
5.2.2 配置 webpack,添加脚本命令 {#5.2.2-%E9%85%8D%E7%BD%AE-webpack%EF%BC%8C%E6%B7%BB%E5%8A%A0%E8%84%9A%E6%9C%AC%E5%91%BD%E4%BB%A4}
data-struct-js/examples/webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const htmlWebpackPlugin = new HtmlWebpackPlugin({
template: path.join(__dirname, "./src/index.html"),
filename: "./index.html"
});
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
`module.exports = {
mode: 'development',
entry: path.join(__dirname, "./src/index.js"),
output: {
path: path.join(__dirname, "dist/"),
filename: "[name].[hash:6].js"
},
module: {
rules: [
{
test: /.(js|jsx)$/,
use: "babel-loader",
exclude: /node_modules/
}
]
},
plugins: [
htmlWebpackPlugin, new CleanWebpackPlugin()
],
resolve: {
extensions: [".js", ".jsx"]
},
devServer: {
port: 8080
}
};
`
data-struct-js/examples/package.json
// ...略...
"scripts": {
"start": "webpack-dev-server --open",
"build": "webpack --config webpack.config.js"
},
// ...略...
5.2.3 编写简单的测试内容,启动并查看 {#5.2.3-%E7%BC%96%E5%86%99%E7%AE%80%E5%8D%95%E7%9A%84%E6%B5%8B%E8%AF%95%E5%86%85%E5%AE%B9%EF%BC%8C%E5%90%AF%E5%8A%A8%E5%B9%B6%E6%9F%A5%E7%9C%8B}
data-struct-js/examples/src/index.js
// 直接引入原文件
import { Stack } from '../../src/Stack'
var stack = new Stack()
for (var i = 0; i \< 5; i++) {
stack.push(i)
}
console.log('isEmpty: ', stack.isEmpty)
console.log('size: ', stack.size)
console.log(stack.toString())
console.log(`pop:`, stack.pop())
stack.traverse((ele,index)=\>{
console.log(`Traversing-Stack:${index}: ${ele}`)
})
stack.traverse((ele,index)=\>{
console.log(`Traversing-Stack:${index}: ${ele}`)
}, true)
`console.log(``claer:``, stack.clear())
console.log('isEmpty: ', stack.isEmpty)
console.log('size: ', stack.size)
`
data-struct-js/examples/src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Demo of data-struct-js</title>
</head>
<body>
</body>
</html>
5.3 确认运行结果 {#5.3-%E7%A1%AE%E8%AE%A4%E8%BF%90%E8%A1%8C%E7%BB%93%E6%9E%9C}
执行我们配置的脚本命令,
启动本地服务,并打开网页:
注意当前目录:data-struct-js/examples
npm run start
将输出结果附在代码后面方便阅读
// 直接引入原文件
import { Stack } from '../../src/Stack'
var stack = new Stack()
for (var i = 0; i \< 5; i++) {
stack.push(i)
}
console.log('isEmpty: ', stack.isEmpty)
// isEmpty: false
console.log('size: ', stack.size)
// size: 5
console.log(stack.toString())
// 0 1 2 3 4
console.log(`pop:`, stack.pop())
// pop: 4
stack.traverse((ele,index)=\>{
console.log(`Traversing-Stack:${index}: ${ele}`)
})
// Traversing-Stack:0: 0
// Traversing-Stack:1: 1
// Traversing-Stack:2: 2
// Traversing-Stack:3: 3
stack.traverse((ele,index)=\>{
console.log(`Traversing-Stack:${index}: ${ele}`)
}, true)
// Traversing-Stack:3: 3
// Traversing-Stack:2: 2
// Traversing-Stack:1: 1
// Traversing-Stack:0: 0
console.log(`claer:`, stack.clear())
// claer: undefined
console.log('isEmpty: ', stack.isEmpty)
// isEmpty: true
`console.log('size: ', stack.size)
// size: 0
`
TODO
这样就能够简单地确认结果了,
之后我们再考虑使用测试框架。
结果表明运行正确符合预期。
这样我们就可以着手发布了。
- 发布到 NPM 上 {#6.-%E5%8F%91%E5%B8%83%E5%88%B0-npm-%E4%B8%8A}
注:路径回到本项目根目录,即 data-struct-js/
6.1 发布须知 {#6.1-%E5%8F%91%E5%B8%83%E9%A1%BB%E7%9F%A5}
6.1.1 用户登录 {#6.1.1-%E7%94%A8%E6%88%B7%E7%99%BB%E5%BD%95}
未登录状态下直接发布的话会报以下错误:
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! [no_perms] Private mode enable, only admin can publish
this module [no_perms] Private mode enable,
only admin can publish this module: data-struct-js
登录的话,执行:
npm login
npm login 根据提示输入 username 跟 password,
然后报了错,发现 npm 源用的 taobao,
这里注意:发布 npm 包时,要将源切换到 npm。
npm ERR! code E409
npm ERR! Registry returned 409 for PUT on
https://registry.npm.taobao.org/-/user/org.couchdb.
user:maonianyou: [conflict] User maonianyou already exists
w我这里用的 nrm 来管理 安装源。
切换:
> nrm use npm
Registry has been set to: https://registry.npmjs.org/
成功登录:
> npm login
Username: yourname
Password: (yourpassword)
Email: (this IS public) yourname@somemail.com
Logged in as yourname on https://registry.npmjs.org/.
注册用户时邮箱未验证也是会报错的:
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
6.1.2 不可重名
首先要在 npm 官网检索一下有没有重名的包,
否则发布的话会因没有权限报以下错误:
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! You do not have permission to publish "xxxxxxx". Are you logged in as
the correct user? : xxxxxxxxx
6.2 发布 {#6.2-%E5%8F%91%E5%B8%83}
6.2.1 编译&发布 {#6.2.1-%E7%BC%96%E8%AF%91%26%E5%8F%91%E5%B8%83}
注意:
- 先编译 npm run compile
- 再发布 npm publish
npm run compile
npm publish
为了方便,我们配置一条脚本命令:
data-struct-js/package.json
// ...略...
"scripts": {
"dopublish": "npm run compile && npm publish"
},
// ...略...
以后就可以:
npm run dopublish
6.2.2 发布成功 {#6.2.2-%E5%8F%91%E5%B8%83%E6%88%90%E5%8A%9F}
>npm publish
npm notice
npm notice package: data-struct-js@0.0.1
npm notice === Tarball Contents ===
npm notice 866B package.json
npm notice 55B index.js
npm notice 1.1kB LICENSE
npm notice 26B README.md
npm notice 1.8kB lib/ArrayBasedStruct.js
npm notice 947B lib/common_utils.js
npm notice 5.1kB lib/Stack.js
npm notice === Tarball Details ===
npm notice name: data-struct-js
npm notice version: 0.0.1
npm notice package size: 3.7 kB
npm notice unpacked size: 9.9 kB
npm notice shasum: 2001495314blsd9gaj9g0b7b15aewr6we5207aac
npm notice integrity: sha512-UzOx7tFP8/qJp[...]DPJ1wherlFJyQ==
npm notice total files: 7
npm notice
+ data-struct-js@0.0.1
到 npm 官网查看发布的 data-struct-js
6.3 下载安装 {#6.3-%E4%B8%8B%E8%BD%BD%E5%AE%89%E8%A3%85}
在自己的工程中安装测试一下。
npm install data-struct-js
引入使用
import { Stack } from 'data-struct-js'
参照:
5.3 确认运行结果 {#5.3-%E7%A1%AE%E8%AE%A4%E8%BF%90%E8%A1%8C%E7%BB%93%E6%9E%9C-1}
6.4 撤销发布 {#6.4-%E6%92%A4%E9%94%80%E5%8F%91%E5%B8%83}
6.4.1 撤销整个包 {#6.4.1-%E6%92%A4%E9%94%80%E6%95%B4%E4%B8%AA%E5%8C%85}
npm unpublish --force your-package-name
6.4.2 仅撤销某个版本 {#6.4.2-%E4%BB%85%E6%92%A4%E9%94%80%E6%9F%90%E4%B8%AA%E7%89%88%E6%9C%AC}
npm unpublish <package-name>@<version>
看到网上有说 :"
超过24小时就不能删除了
"
但查看官网,是 72 小时内可撤销发布,
超过 72 小时的就需要联系 npm Support 了。
没有实践确认,以官网说明为准吧
- 关于包的更新 {#7.-%E5%85%B3%E4%BA%8E%E5%8C%85%E7%9A%84%E6%9B%B4%E6%96%B0}
增加功能或者修改bug后,
就要更新我们的 package。
这里主要涉及到版本管理。
7.0 不改版本不能发布 {#7.0-%E4%B8%8D%E6%94%B9%E7%89%88%E6%9C%AC%E4%B8%8D%E8%83%BD%E5%8F%91%E5%B8%83}
修改项目代码后,版本号不变,
继续发布的话,就会报错:
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! You cannot publish over the previously published versions:
0.0.1. : data-struct-js
7.1 语义化版本管理(semantic versioning) {#7.1-%E8%AF%AD%E4%B9%89%E5%8C%96%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86%EF%BC%88semantic-versioning%EF%BC%89}
这里引用 npm 官方文档的表格,
加入部分中文翻译。
| | Code status | Stage | Rule | Example version | |---|----------------------------------------------------|-----------------------|------------------------------------------------------------------------------------|-----------------| | 1 | First release 首次发布 | New product 新产品 | Start with 1.0.0 | 1.0.0 | | 2 | Backward compatible bug fixes 向后兼容 bug 修改 | Patch release 发布补丁更新 | Increment the third digit 第三位数增长 | 1.0.1 | | 3 | Backward compatible new features 向后兼容 增加新特性 | Minor release 发布小版本更新 | Increment the middle digit and reset last digit to zero 中间的版本号增长,同时末尾版本号清零 | 1.1.0 | | 4 | Changes that break backward compatibility 不向后兼容的变更 | Major release 发布主版本更新 | Increment the first digit and reset middle and last digits to zero 首位版本号增长,第二第三都清零 | 2.0.0 |
上面,除了 1 是首次新发布,后面 2 3 4 分别为 patch / minor / major。
7.2 手动修改 version 后发布 {#7.2-%E6%89%8B%E5%8A%A8%E4%BF%AE%E6%94%B9-version-%E5%90%8E%E5%8F%91%E5%B8%83}
data-struct-js/pakcage.json
{
"version": "1.0.1"
}
npm publishs
7.3 自动增加版本号发布 {#7.3-%E8%87%AA%E5%8A%A8%E5%A2%9E%E5%8A%A0%E7%89%88%E6%9C%AC%E5%8F%B7%E5%8F%91%E5%B8%83}
npm version <update_type>
<update_type>为:
- patch 0.0.*
- major *.0.0
- minor 1.*.0
npm version patch
`npm publish
`
以上命令,
就是将我们的包按照语义化的版本管理,
自动变更版本号,然后发布。
- 其它 {#8.-%E5%85%B6%E5%AE%83}
8.1 修改 npm 初始化时的信息 {#8.1-%E4%BF%AE%E6%94%B9-npm-%E5%88%9D%E5%A7%8B%E5%8C%96%E6%97%B6%E7%9A%84%E4%BF%A1%E6%81%AF}
执行以下命令,可修改默认的初始化内容
> npm set init.author.email "example-user@example.com"
> npm set init.author.name "example_user"
> npm set init.license "MIT"
8.2 关于 scoped/unscoped {#8.2-%E5%85%B3%E4%BA%8E-scoped%2Funscoped}
还记得我们上面用到的 babel 相关的几个包吗,
@babel/cli
@babel/core
@babel/preset-env
你会注意到,它的这种格式,
跟安装我们这次开发的包不一样
npm install @babel/cli
`npm install data-struct-js
`
这个 @scopename
就是 scoped 的限定,
如包名:@codermonkey/data-struct-js
因为私有包是收费的,冠名只能设为公开,
所以发布时需加上以下参数:
npm publish --access public
发布出来就是:
@yourscope/packagename
1.0.0 · Public · Published ... ago
Scope 用以关联一系列的包,
每个用户和组织都有自己的 Scope,
前面我们提到发布包不可以重名,
其实如果重名的话,
只要不在同一个 Scope 下应该也是可以的。
不知道会不会违反包名类似的规则
不过也有与本示例稍微类似的包名,我们的还是发布成功了
可以在登录的时候指定 scope 名称:
npm login --registry=http://reg.example.com --scope=@myco
也可以在 config 中配置指定源的 scope:
npm config set @myco:registry http://reg.example.com
更多关于 Scope 详情,请参看官网:
*注:本文参考了 NPM 官网文档