从零搭建webpack前端类库脚手架[6]-在github上开源

一个项目自己用那就无所谓好与不好,也无所谓做任何其他工作,只要自己用的好用就OK了。然而要把项目开源,我们要做很多事情。

比如

  1. 安装方法要步骤清晰可用,文档要清晰易懂,quickStart要简洁明了,
  2. 而且代码要经过测试。
  3. 最好,还有给你的项目做上logo和一些亮眼的简洁来吸引用户。
  4. github上一些常用的徽标也自然不能少,起码让人一眼望去便知道你的代码测试、下载量等等都是没问题的。
  5. 要提供清晰的License避免法律问题
  6. 要提供contribute的方法
  7. 要关注issue,及时修复用户的bug

否则,你开个毛线的源呢,嘿嘿(其实我就开了很多毛线的源,逃..)

下面,我们依次介绍其中技术方面的一些方法。可以说,本文涵盖了在github上面开源一个JavaScript技术项目的大部分工作内容。

github工作流

使用git进行协作开发是目前主流的方式。git的强大能力也使其已经成为目前社区和业界开源项目的主流版本管理工具。

由于github的工作流涉及稍微多一点的内容,故我将单独在另外一篇博文中学习: github入门训练营[5]-工作流的最佳实践

这里稍微提一句,对于简单的git项目来说,可能只有1-2个人,其实直接使用master分支进行无脑开发也无伤大雅。但是对于具有更高追求的开发者来说,推荐使用更好的github workflow实践。

使用.npmignore.gitignore

源码托管与构建发布的内容,在很多时候是不同的。尤其是在需要编译的前端项目中,一般github上要托管源码进行版本控制,而将该前端库发布到npm供别人使用时,则不需要发布源码,而是要发布出编译后的内容。

体现在具体的项目中,我们需要将项目中的src目录push到github上进行托管,而却不能把编译出来的dist发布到github。我们需要将项目中的编译出来的dist目录发布到npm仓库,但不能把src源码目录发布到npm仓库。

利用.gitignore可以设置git的忽略文件和目录,通过它我们就可以让git把指定的目录忽略,避开github托管。下面是我的 .gitignore 配置:

http://front-ender.me/architecture/npmignore.html

发现files白名单就能解决发布到npm有哪些文件的问题,.npmignore可以提供一个辅助,其实有了白名单不需要npmignore了
https://cnodejs.org/topic/581d831de90cfbec054d76b9#59ddbeb520a1a3647d72aaee

在根目录下面的npmingore是不会覆盖files已经列为白名单的目录和文件的。也就是他无法排除掉files写的文件,但是在子目录里面可以。
At the root of your package it will not override the “files” field, but in subdirectories it will. The .npmignore file works just like a .gitignore. If there is a .gitignore file, and .npmignore is missing, .gitignore’s contents will be used instead.

完善package.json

1
2
3
4
5
6
"engines": {
"node": ">=0.10.3",
"npm": ">=1.0.20"
},
"os": ["darwin", "linux", "win32"],
"cpu": ["x64", "x86", "ia32", "arm"]

ES6 module设定

https://github.com/rollup/rollup/wiki/pkg.module

But for ES2015-aware tools like Rollup, using the CommonJS (or Universal Module Definition) build isn’t ideal, because we can’t then take advantage of ES2015 module features. So assuming that you’ve written your package as ES2015 modules, you can generate an ES2015 module build alongside your CommonJS/UMD build:

{
“name”: “my-package”,
“version”: “0.1.0”,
“main”: “dist/my-package.umd.js”,
“module”: “dist/my-package.esm.js”
}
Now we’re cooking with gas - my-package continues to work for everyone using legacy module formats, but people using ES2015 module bundlers such as Rollup don’t have to compromise. Everyone wins!

依赖锁版本

lock 文件则是整个依赖关系树的快照,包含了所有包及其解析的版本。

文件可能会长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"name": "package-name",
"version": "1.0.0",
"lockfileVersion": 1,
"dependencies": {
"cacache": {
"version": "9.2.6",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-9.2.6.tgz",
"integrity": "sha512-YK0Z5Np5t755edPL6gfdCeGxtU0rcW/DBhYhYVDckT+7AFkCCtedf2zru5NRbBLFk6e7Agi/RaqTOAfiaipUfg=="
},
"duplexify": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz",
"integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=",
"dependencies": {
"end-of-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz",
"integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4="
},

lock 文件现在包含一个 integrity 字段,它使用 Subresource Integrity 来验证已安装的软件包是否被改动过,换句话来说,验证包是否已失效。它依旧支持旧版本 NPM 中对包的加密算法 SHA-1,但是以后将默认使用 SHA-512 进行加密. 其实就是通过integrity来表示包的SHA1值,从而能在安装时进行验证。

你可能已经注意到了,指向特定 URI 的文件的 resolved 字段仍然得到了保留。注意,NPM 现在可以(根据 .npmrc 中的设置)解析机器配置使用的不同仓库,这样的话,与 integrity 字段一起配合,只要签名是匹配的,包的来源并无关紧要。 其实也就是说你安装时可能是从淘宝的npm源上下载的包,但只要SHA1符合integrity,就证明包是对的。

该文件现在增加了 lockfileVersion 字段来指定的 lock 格式的版本,并将其设置为1。这是为了使将来的格式更新时,不用去猜测该文件使用什么特定版本。以前的 lock 格式仍然支持并被识别为版本 0

lock文件使得即使不同开发人员进行开发,他们本地也能安装完全相同的依赖版本。现在5.x的npm和yarn,在lock文件里都是用 打平 的方式来描述包的依赖。

在最新版的npm中,只要执行npm安装依赖的操作,就会自动生成package-lock.json这个文件。建议把package-lock.json放置到git仓库中进行管理。

接下来,您可能会想知道在已经包含 package-lock.json 的目录中运行 npm shrinkwrap 时会发生什么。答案很简单,NPM 仅仅会把 package-lock.json 重命名为 npm-shrinkwrap.json。因为文件的格式是完全一样的。

最好奇的还会问,当两个文件都存在时会发生什么。 在这种情况下,NPM将完全忽略 package-lock.json,只使用 npm-shrinkwrap.json。 当只使用 NPM 操纵文件时,这种情况不应该发生。

Yarn中的机制跟npm5.x是类似的,当你运行 yarn 或 yarn add 之一时,Yarn 会在你的包根目录里生成一个 yarn.lock 文件。 你不需要读懂这个文件 - 只要把它提交到代码管理系统里。 当另一个人开始用 Yarn 代替 npm 时,yarn.lock 将保证他们和你一样得到精确相同的依赖。
参考https://yarnpkg.com/lang/zh-hans/docs/migrating-from-npm/

如果你正在使用 npm-shrinkwrap.json 文件,请注意,你可能最终得到一组不同的依赖项。 Yarn 不支持 npm shrinkwrap 文件,因为它们里面没有足够的信息来支撑 Yarn 更精确的算法。 如果你正在使用 shrinkwrap 文件,它可能更容易让这个项目的每个人同时转换到 Yarn。 只需删除你已有的 npm-shrinkwrap.json 文件并提交新创建的 yarn.lock 文件。

发布npm时的处理

发布到npm时,如果是库,则一般不要发布package-lock.json文件,因为对方应该想利用npm可以自由安装符合semver版本的特性优势,而开发node应用时可能最好要把lock文件发布出去。 详情可以查看这篇文章: https://juejin.im/post/5943849aac502e006b84ce07

npm发布

https://unpkg.com/#/

files字段
https://docs.npmjs.com/files/package.json#files
npmignore
At the root of your package it will not override the “files” field, but in subdirectories it will

https://segmentfault.com/q/1010000011509177
files和npmignore的取舍:
可以看到vue.js里只使用了files,我认为黑名单和白名单2者使用其一即可。
由于我们所有的.babelrc, .editorconfig, package-lock.json这些开发用的文件都不需要发布到npm包仓库,所以我们更适合采用白名单机制,在files字段中指定我们要发布的目录和文件。

前端库一般把src目录也放进去,方便别人webpack直接引用原始代码打包(而不直接引用build后的版本),这样其实有时候更能使用上调用方的webpack tree-shaking能力。 当然我们应该像vue那样直接编译一个esmodule类型的bundle出来,因为直接让调用者引用源码是不太好的,毕竟调用者不一定配置好了你代码中用到的各种polyfill,他可能仅仅支持了esmodule的引用方式而已(比如单纯的未配置babel的rollup工具)

unpkg字段: 一般是你是通过 https://unpkg.com/jquery@3.3.1/dist/jquery.js 这样来访问unpkg的cdn文件的;而如果用户省略了版本号则默认会取 latestVersion,如果省略了 dist/jquery.js 呢,这时会默认寻找package.json中的main字段指定的js,如果你指定了 unpkg字段,则会返回 unpkg字段指定的文件。

1
2
3
{
"unpkg": "dist/vue-router.min.js"
}

unpkg工作流

https://unpkg.com/#/

editorconfig配置

.editorconfig 配置文件可以定义编辑器的一些风格配置。各个主流的编辑器,都有对应的插件可以识别这个文件,并自动进行代码格式化。 在vscode中,可以搜索editorconfig来找到对应的插件安装即可。

从editorconfig官网可以了解到其配置方法,该文件是采用ini配置的格式进行设置的,我的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

# markdown 文件不删除行末尾的空格
[*.md]
trim_trailing_whitespace = false




# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,py}]
charset = utf-8

# 4 space indentation
[*.py]
indent_style = space
indent_size = 4

# Tab indentation (no size specified)
[Makefile]
indent_style = tab

# Indentation override for all JS under lib directory
[lib/**.js]
indent_style = space
indent_size = 2

# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

前端库的分发

npm自带了一个cdn地址,可以自动从npm仓库中返回你指定的js文件。

也可以使用jsdelivr.net https://www.jsdelivr.com/?docs=gh

README, CHANGELOG

CHANGELOG.md

ChangeLog

3.2.6

  • 【删除】- 删除 3 个自带的 filter:yesToBe / yesOrNoToBe / nullToBe
  • 【变更】- 组件 compile 过程明确属于 init,compiled 生命周期在 inited 前到达
  • 【变更】- 组件初始化传入的 data 与 initData 返回值进行合并,作为组件初始化数据值
  • 【新特性】- 针对希望自建组件渲染体系的需求,暴露新的 API:
    • {Object} ExprType
    • {Class} LifeCycle
    • {ANode} parseTemplate({string} source)
    • {Object} parseExpr({string} source)

LICENSE直接用github生成的即可。

README有一个模板,微信收藏。前端库的README可参考https://github.com/jrainlau/rhyke

代码规范

无论是 README,还是代码,都有一定的代码规范,关于代码规范推荐参考百度 fex 的 开源代码规范 和 ecomefe 的代码规范. 两者是相似的,只是 ecomfe 的种类更全一些。

徽标

在很多开源项目中,都会在README.md中设置上一些徽标,从而让自己项目的各项指标情况一目了然。例如: 版本、下载量、测试是否成功、以及测试覆盖率等。

这些图标,可以通过不同的网站来生成。

对于持续集成的这个图标,它是基于 Travis CI 持续集成工具来生成的。在Travis CI会有与github上仓库一一对应的一个仓库,在Travis上面设置对该项目进行编译后,可以在Travis的项目页面上直接拷贝这个徽标代码,然后粘贴到github的 README.md 文件中即可。

如果想放置一个npm包的logo图标,可以去这里生成一个: https://nodei.co/

对于license,下载量等图标,都是去这里:https://shields.io/category/license

找到对应类型的图标点进去,然后点他列表中的类型:如github的包、npm的包。
我们的包一般是npm的,所以我们点npm类型,然后他会让你输入包名,然后他就能搜到你的包,生成对应的License图标。

还可以添加 DevDependencies的更新情况的徽标。可以参考:https://github.com/isaacs/rimraf

持续集成Travis CI

  1. 激活github仓库
    一旦你登陆了travis-ci.org,Travis就会同步你github上面所有仓库,然后我们可以在Travis的Profile页面看到这些开源的项目(包括所有你有权限访问以及有权限进行hook的)。 如果你想对哪个项目进行Travis持续集成,你只需要在该页面上勾选这个项目前面的钩钩,就可以自动注入Travis的hook了。

  2. 添加 .travis.yml 文件到你的github仓库中
    为了让Travis CI可以编译你的项目,你需要告诉Travis系统一些环境信息,所以你就需要在你项目根目录中通过 .travis.yml 来指定这些信息。

如果 .travis.yml 文件不存在,或者写错了,或者不是合法的YAML文件,那么Travis CI读取时就会忽略这个文件。

JavaScript的Travis配置参考,可以在这个页面查看。

  1. 用git push命令触发你的第一次构建(编译)
    一旦github的hook建立后,你可以把刚刚添加的 .travis.yml push到你的github仓库。这样就会触发Travis对该仓库项目的构建,此时会有一个build任务被放到Travis CI的构建队列中,等到Travis上面的JavaScript语言构建worker空闲时就会启动你的构建任务。

在其他时候,如果想启动一次构建,可以去你仓库的设置页面,点击左边的 Webhooks & services,然后选择Services中的 Travis CI,再点 Test service 按钮。

NOTE: 第一次构建不能用Test Hook按钮,必须使用push的方法来触发第一次构建

下面是我的Travis配置:

1
2
3
4
5
6
7
8
9
10
language: node_js
node_js:
- "6"
os: linux
cache:
yarn: true
branched:
only:
- master
- dev

构建触发后,在你的Travis仓库里就能看到这个项目的编译过程和日志信息了, 比如这里有我的项目的Travis仓库页面: https://travis-ci.org/cuiyongjian/fetch-file。 在Travis的这个仓库页面,你可以对该仓库的编译过程进行start,restart等操作。而且可以看到Travis把我们的yml配置文件信息,读取为一个JOSN格式的config:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"language": "node_js",
"node_js": "6",
"os": "linux",
"cache": {
"yarn": true
},
"branched": {
"only": [
"master",
"dev"
]
},
"group": "stable",
"dist": "trusty"
}

修改编译分支的逻辑:https://docs.travis-ci.com/user/customizing-the-build/

社区交流

可以像vue或 https://github.com/pure-css/pure/的官方仓库一样,放置gitter或者其他交流软件的徽标,让大家去交流。中国可以放微信二维码或qq群。

Refer

http://www.tuicool.com/articles/7rEJRjB