清除 不必要 文件 在 node_modules (.md, .ts, ...) 中
Explanation
"version": "1.0.1"
go 语言中,一个库能被命令方式使用,必然需要
node-prune/cmd/node-prune/main.go
package main // 作为编译-命令-程序的入口
我们就从 main.go
开始
代码 1-12
package main
import (
"flag" // 原生-命令解析 :> $ node-prune --help
"fmt" // 原生-终端屏幕输出 :> 就像 js{console} py{print()}
"time" // 原生-时间 :>
"github.com/apex/log" // 作者所写,日志记录
"github.com/apex/log/handlers/cli"
"github.com/dustin/go-humanize"
"github.com/tj/node-prune" // 引入-node-prune-主库
)
-
go 语言在,库与库之间的应用上
-
决心,铲除异己,一个库只能存在一个版本
-
而 所有库都存放在,
GOPATH
的 环境变量路径中, -
这就决定了,库的
唯一版本
且存放的体积
, 而不是node_modules
的无尽深渊
如果 .zshrc | .bashrc
export GOPATH='path/to/gopath/
那么 "github.com/apex/log"
就在
GOPATH/src/github.com/apex/log //中
// 类推
//"github.com/apex/log/handlers/cli"
GOPATH/src/github.com/apex/log/handlers/cli
// ...
当然,GOPATH 的可以是多个路径,一个路径一个集体
❕下文,有些例子,那么你需要 clone
这 explain
到你的 GOPATH
go get -v github.com/chinanf-boy/explain-node-prune
代码 15-17
func init() {
log.SetHandler(cli.Default)
log.SetLevel(log.WarnLevel)
}
- log
很明显,
log
从哪里来, 那么我们需要看下去👀
import (
// ...
"github.com/apex/log"
// ...
)
-
❓ 这样的导入, 意味着什么
-
ℹ️ 意味着-
import
-> 导入package log
-> 🉐️log
-
㊙️ 这个
log
代表了,整个库的内容,不过只有开头大写
才是公有的
log.SetHandler(cli.Default) // S 大写
- log.SetHandler(cli.Default)
日志设置出口
cli.go
var Default = New(os.Stderr) //写入标准错误以防止混乱和崩溃
- log.SetLevel(log.WarnLevel)
日志设置等级
- log.WarnLevel
const (
InvalidLevel Level = iota - 1 // iota==0
// 用一次 + 1
DebugLevel // 0
InfoLevel // 1
WarnLevel // 2
ErrorLevel // 3
FatalLevel // 4
)
使用
flag
对 用户的输出解析
代码 20-35
func main() {
debug := flag.Bool("verbose", false, "Verbose log output.")
// 在所有flag都注册之后
flag.Parse() // 来解析命令行参数写入注册的flag里。
dir := flag.Arg(0) // 第一个命令选项
start := time.Now() // 现在时间
// : 2009-11-10 23:00:00 +0000 UTC m=+0.000000001
if *debug {
log.SetLevel(log.DebugLevel)
}
var options []prune.Option
// 一个 特定-容器
if dir != "" {
options = append(options, prune.WithDir(dir))
}
// prune.WithDir(dir) 返回 一个 特定-结构 被 特定容器装下
// append : options-数组->推入-> prune.WithDir(dir)-数值
- flag.Bool(a,b,c)
- a ==
verbose
$node-prune verbose # 触发
- b ==
false
默认值
- c == "Verbose log output."
$node-prune --help
Usage of node-prune:
-verbose
Verbose log output.
- prune.WithDir(dir)
func WithDir(s string) Option {
return func(v *Pruner) {
v.dir = s
}
}
go 语言的类型与接口是固定程序的强大工具
但是,一般不熟悉,相关类型与接口的,却可以说疲于奔命
type Pruner struct {
dir string
log log.Interface
dirs map[string]struct{}
exts map[string]struct{}
files map[string]struct{}
ch chan func()
wg sync.WaitGroup
}
// Option function.
type Option func(*Pruner)
在这一段,说明白对谁都好👀
-
func WithDir(s string) Option
-
s string
| s-函数变量名, 变量类型-string -
Option
| 返回 Option 类型
-
-
type Option func(*Pruner)
-
type
| 定义类型-关键字 -
Option
| 类型名字 -
func(*Pruner)
| 对应类型 - 函数 - 其中变量需要是*Pruner
-
-
type Pruner struct
如果说上面的类型-只不过是一对一指代关系
-
�struct
| 那么 struct 就是一对多的关系 -
这里就需要称
Pruner
为结构
啦
-
type Pruner struct { // Pruner 结构
dir string // dir-类型名, string-类型
log log.Interface
dirs map[string]struct{}
exts map[string]struct{}
files map[string]struct{}
ch chan func()
wg sync.WaitGroup
}
func WithDir(s string) Option {
return func(v *Pruner) {
v.dir = s
}
}
-
prune.WithDir(dir)
返回一个 函数类型Option
-
又因为
type Option func(*Pruner)
-
所以其实
Option
==func(*Pruner)
-
func(*Pruner)
函数的变量是Pruner
结构 -
Pruner
结构中成分-*Pruner.dir
=dir
dir
作为第一层WithDir
的变量s
, 附值给第二层func的
变量v *Prune
中的结构成分-v.dir
🦊 真累啊, 想不到怎么难说 😢 , 继续前进吧
新建
prune
实例并运行
代码 37-42
p := prune.New(options...)
stats, err := p.Prune()
if err != nil {
log.Fatalf("error: %s", err)
}
展示运行后数据
代码 44-56
println()
defer println() // 1
output("files total", humanize.Comma(stats.FilesTotal))
output("files removed", humanize.Comma(stats.FilesRemoved))
output("size removed", humanize.Bytes(uint64(stats.SizeRemoved)))
output("duration", tim·e.Since(start).Round(time.Millisecond).String())
}
func output(name, val string) {
fmt.Printf("\x1b[1m%20s\x1b[0m %s\n", name, val)
}
defer
: 总是在函数退出前运行
可用来,数据库的断开,文件关闭,等等
humanize
转类型