@h5l0/codelens 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -48
- package/README.zh.md +59 -49
- package/dist/cli/index.js +10 -6
- package/dist/core/profile.js +54 -31
- package/dist/web/assets/index-CRwiLphE.js +9 -0
- package/dist/web/assets/index-iMj81ozS.css +1 -0
- package/dist/web/index.html +2 -2
- package/docs/screenshots/en/calendar.png +0 -0
- package/docs/screenshots/en/loc.png +0 -0
- package/docs/screenshots/zh/calendar.png +0 -0
- package/docs/screenshots/zh/loc.png +0 -0
- package/package.json +1 -1
- package/dist/web/assets/index-B4yfgYFQ.css +0 -1
- package/dist/web/assets/index-BIIwVDXB.js +0 -9
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ npm install -g @h5l0/codelens
|
|
|
23
23
|
codelens
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
No configuration is needed.
|
|
26
|
+
No configuration is needed. codelens collects the data at startup and writes nothing to disk by default.
|
|
27
27
|
|
|
28
28
|
## The two views
|
|
29
29
|
|
|
@@ -44,14 +44,14 @@ A treemap: each rectangle's area is its line count, its color is the category, a
|
|
|
44
44
|
```
|
|
45
45
|
codelens [directory] [options]
|
|
46
46
|
|
|
47
|
-
--profile <name|file> Profile, see below
|
|
47
|
+
--profile <name|file> Profile, see below. Built in: all, web
|
|
48
48
|
--config <file> Config file, defaults to <directory>/codelens.config.json
|
|
49
49
|
--days <days> Calendar time span, 0 means full history (default 120)
|
|
50
50
|
--exclude <glob> Extra paths to ignore, repeatable
|
|
51
51
|
--port <port> Listen port, default 5178, tries the next ports when busy
|
|
52
|
-
--host <address> Listen address, default 127.0.0.1
|
|
52
|
+
--host <address> Listen address, default 127.0.0.1. Other addresses expose the page to your network
|
|
53
53
|
--no-open Do not open the browser automatically
|
|
54
|
-
--no-gitignore
|
|
54
|
+
--no-gitignore Do not filter by .gitignore. Only the built-in dependency and build directories are skipped
|
|
55
55
|
--dump <dir> Write data.json and loc.json, then exit without starting a server
|
|
56
56
|
--dev Dev mode with Vite hot reload
|
|
57
57
|
-h, --help Show help
|
|
@@ -60,77 +60,86 @@ codelens [directory] [options]
|
|
|
60
60
|
|
|
61
61
|
## Profiles
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
A profile controls how codelens divides the repository. The calendar colors commits by **group** (`groups`). The treemap colors files by **category** (`categories`).
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
2. Or use a name under `profiles` in `codelens.config.json`. Use `--config` to move that file: `--profile web`.
|
|
65
|
+
`--profile` takes a file or a profile name:
|
|
67
66
|
|
|
68
|
-
|
|
67
|
+
```bash
|
|
68
|
+
codelens --profile ./my-profile.json # use this file as the profile
|
|
69
|
+
codelens --profile web # use the web profile from the config
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Two profiles come with codelens:
|
|
73
|
+
|
|
74
|
+
| Name | Shown in the page as | What it does |
|
|
75
|
+
| ------- | ---------------------- | ------------------------------------------------------------------------------------------------- |
|
|
76
|
+
| `all` | All | The default profile. It has no groups and counts the whole repository. |
|
|
77
|
+
| `web` | Frontend / backend | Directories such as `frontend/`, `web/`, `client/` and `ui/` are frontend. The rest is backend. |
|
|
69
78
|
|
|
70
|
-
|
|
71
|
-
| --- | --- |
|
|
72
|
-
| `all` | Default; no groups, the tool counts the whole repository together |
|
|
73
|
-
| `web` | `frontend/`, `web/`, `client/`, `ui/` and similar directories count as frontend, everything else as backend |
|
|
79
|
+
### Config
|
|
74
80
|
|
|
75
|
-
|
|
81
|
+
The config controls the default behavior of codelens and holds a list of profiles. codelens loads `codelens.config.json` from the workspace root by default. Use `--config` to point at another file.
|
|
76
82
|
|
|
77
83
|
```jsonc
|
|
78
84
|
{
|
|
79
|
-
"profiles":
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"groups": [
|
|
85
|
+
"profiles": [
|
|
86
|
+
{
|
|
87
|
+
"id": "modules",
|
|
88
|
+
"label": "By module", // shown in the startup log
|
|
89
|
+
"groups": [ // calendar colors, first match wins
|
|
85
90
|
{ "id": "core", "label": "Core", "hue": 214, "sat": 58, "match": ["src/core/**"] },
|
|
86
91
|
{ "id": "web", "label": "UI", "hue": 152, "sat": 46, "match": ["src/web/**"] },
|
|
87
|
-
{ "id": "
|
|
92
|
+
{ "id": "rest", "label": "Rest", "hue": 32, "sat": 62, "match": ["**"] }
|
|
88
93
|
],
|
|
89
|
-
|
|
90
|
-
"categories": [
|
|
94
|
+
"categories": [ // treemap colors and the legend
|
|
91
95
|
{ "id": "core", "label": "Core code", "hue": 214, "sat": 58, "match": ["src/core/**"] },
|
|
92
96
|
{ "id": "test", "label": "Tests", "hue": 152, "sat": 46, "defaultOn": false, "match": ["**/*.test.ts"] },
|
|
93
97
|
{ "id": "app", "label": "Other code", "hue": 220, "sat": 20 }
|
|
94
98
|
],
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
+
"ignore": ["data/**", "**/*.snap"] // extra paths to skip
|
|
100
|
+
},
|
|
101
|
+
]
|
|
99
102
|
}
|
|
100
103
|
```
|
|
101
104
|
|
|
102
|
-
Field
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
105
|
+
| Field | Meaning |
|
|
106
|
+
| ----------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
107
|
+
| `profiles[*].id` | The name of the profile. This is what `--profile` takes. It must be unique in one config. |
|
|
108
|
+
| `profiles[*].label` | The name shown in the startup log. Without it, codelens joins the group names. |
|
|
109
|
+
| `profiles[*].{groups/categories}[*].id` | An identifier. It must be unique in its own list, and it must not be `all`. |
|
|
110
|
+
| `profiles[*].{groups/categories}[*].label` | The name shown in the page. Your own text is shown as written. |
|
|
111
|
+
| `profiles[*].{groups/categories}[*].match` | Path globs. An item joins the entry it matches. Required for groups, optional for categories. |
|
|
112
|
+
| `profiles[*].{groups/categories}[*].hue`, `sat` | The color of the entry, in HSL. The defaults are 214 and 50. |
|
|
113
|
+
| `profiles[*].categories[*].defaultOn` | Set it to `false` and the category is off in the legend at first. Readers can turn it on. |
|
|
114
|
+
| `profiles[*].ignore` | Extra paths to skip. It is merged with `--exclude`. |
|
|
115
|
+
|
|
116
|
+
Notes:
|
|
117
|
+
|
|
118
|
+
- Glob paths are relative to the repository root. `**` crosses directories. `*` and `?` stay within one segment. `{a,b}` matches either one.
|
|
119
|
+
- A pattern without `/` matches that name at any depth. `/foo` counts from the repository root. `foo/` means the directory and everything inside it.
|
|
120
|
+
- The config accepts `//` and `/* */` comments and trailing commas.
|
|
121
|
+
- Without `categories`, codelens uses six built-in ones. They are application code, tests, scripts, docs, config and generated code. Of these, "docs", "config" and "generated code" are off at first.
|
|
122
|
+
- A profile name that the config does not define falls back to a built-in profile. If neither has the name, codelens reports an error.
|
|
113
123
|
|
|
114
124
|
## Counting rules
|
|
115
125
|
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
- `--exclude` and the config's `ignore`
|
|
119
|
-
-
|
|
120
|
-
- The calendar puts each commit in the day of its commit time (committer date).
|
|
121
|
-
- Merge commits, empty commits and commits that only change file modes or binaries have no line counts. They still appear in the commit list and count as commits.
|
|
122
|
-
-
|
|
126
|
+
- codelens filters by `.gitignore` by default. If the directory is not a git repository, codelens applies equal ignore rules. `--no-gitignore` turns the filter off.
|
|
127
|
+
- codelens always skips dependency and build directories such as `node_modules`, `dist`, `build`, `coverage`, `.venv`, `__pycache__` and `target`.
|
|
128
|
+
- `--exclude` and the config's `ignore` affect both views. An excluded directory has no line counts and does not appear in the calendar.
|
|
129
|
+
- codelens skips binary files, files larger than 3MB and empty files. It skips a file it cannot read and reports the number in the startup log. Line counts are physical lines. The newline at the end of a file does not add a line.
|
|
130
|
+
- The calendar puts each commit in the day of its commit time (committer date). `--days` filters by the same time. "Changed lines" means insertions plus deletions.
|
|
131
|
+
- Merge commits, empty commits and commits that only change file modes or binaries have no line counts. They still appear in the commit list, and they count as commits.
|
|
132
|
+
- When you run codelens on a subdirectory of a repository, both views count that subdirectory only and resolve paths relative to it.
|
|
123
133
|
|
|
124
134
|
## Known limits
|
|
125
135
|
|
|
126
|
-
- The change calendar needs git.
|
|
136
|
+
- The change calendar needs git. Without commits or without git, the calendar is empty. The lines view still works.
|
|
137
|
+
- The treemap draws at most 6000 rectangles. It does not show the rest.
|
|
127
138
|
- Line counts say nothing about code complexity.
|
|
128
|
-
- The treemap draws at most 6000 rectangles. The rest are not shown.
|
|
129
|
-
- The calendar is padded to whole weeks. A window that does not start or end on a week boundary shows a few extra days without data, with a paler background.
|
|
130
139
|
|
|
131
140
|
## Languages
|
|
132
141
|
|
|
133
|
-
The page follows the browser language. Simplified Chinese, English, Japanese and Korean are built in.
|
|
142
|
+
The page follows the browser language. Simplified Chinese, English, Japanese and Korean are built in. English is the default. Append `?lang={langCode}` to the URL to override the language, for example `?lang=zh` or `?lang=ko`.
|
|
134
143
|
|
|
135
144
|
## Development
|
|
136
145
|
|
package/README.zh.md
CHANGED
|
@@ -23,7 +23,7 @@ npm install -g @h5l0/codelens
|
|
|
23
23
|
codelens
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
无需配置。启动时生成数据,默认不写入硬盘。
|
|
27
27
|
|
|
28
28
|
## 两个视图
|
|
29
29
|
|
|
@@ -44,93 +44,103 @@ codelens
|
|
|
44
44
|
```
|
|
45
45
|
codelens [目录] [选项]
|
|
46
46
|
|
|
47
|
-
--profile <名称|文件>
|
|
48
|
-
--config <文件>
|
|
47
|
+
--profile <名称|文件> 预设,见下节,内置 all、web
|
|
48
|
+
--config <文件> 配置,默认 <目录>/codelens.config.json
|
|
49
49
|
--days <天数> 日历时间跨度,0 表示全部历史(默认 120)
|
|
50
50
|
--exclude <glob> 额外忽略的路径,可重复
|
|
51
51
|
--port <端口> 监听端口,默认 5178,被占用时向后尝试
|
|
52
|
-
--host <地址> 监听地址,默认 127.0.0.1
|
|
52
|
+
--host <地址> 监听地址,默认 127.0.0.1。监听其他地址时,页面对同网段可见
|
|
53
53
|
--no-open 不自动打开浏览器
|
|
54
|
-
--no-gitignore 不按 .gitignore
|
|
54
|
+
--no-gitignore 不按 .gitignore 过滤,只跳过内置的依赖与构建目录
|
|
55
55
|
--dump <目录> 只写出 data.json 与 loc.json 后退出,不启动服务
|
|
56
56
|
--dev 开发模式,用 Vite 托管前端源码并热更新
|
|
57
57
|
-h, --help 显示帮助
|
|
58
58
|
-v, --version 显示版本
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
##
|
|
61
|
+
## 预设(profile)
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
预设用于控制统计的划分方式。改动日历按**分组**(`groups`)给提交上色。行数视图按**分类**(`categories`)给文件上色。
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
2. 取 `codelens.config.json` 里 `profiles` 下的档名,位置可用 `--config` 改:`--profile web`。
|
|
65
|
+
`--profile` 后面可以写文件,也可以写预设名:
|
|
67
66
|
|
|
68
|
-
|
|
67
|
+
```bash
|
|
68
|
+
codelens --profile ./my-profile.json # 用这个文件当预设
|
|
69
|
+
codelens --profile web # 用配置里的 web 预设
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
codelens 自带两个预设:
|
|
69
73
|
|
|
70
|
-
| 名称 | 作用
|
|
71
|
-
|
|
|
72
|
-
| `all` |
|
|
73
|
-
| `web` | `frontend/`、`web/`、`client/`、`ui/`
|
|
74
|
+
| 名称 | 页面上显示为 | 作用 |
|
|
75
|
+
| ------- | -------------- | ---------------------------------------------------------------- |
|
|
76
|
+
| `all` | 全部 | 默认预设,不分组,统计整个仓库。 |
|
|
77
|
+
| `web` | 前后端 | `frontend/`、`web/`、`client/`、`ui/` 算前端。其余目录算后端。 |
|
|
74
78
|
|
|
75
|
-
###
|
|
79
|
+
### 配置
|
|
80
|
+
|
|
81
|
+
配置用于控制程序默认行为,可包含一组预设。
|
|
82
|
+
默认加载工作区根目录的配置文件:`codelens.config.json`,用 `--config` 可以指定别的文件。
|
|
76
83
|
|
|
77
84
|
```jsonc
|
|
78
85
|
{
|
|
79
|
-
"profiles":
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
"groups": [
|
|
86
|
+
"profiles": [
|
|
87
|
+
{
|
|
88
|
+
"id": "modules",
|
|
89
|
+
"label": "按模块", // 显示在启动日志里
|
|
90
|
+
"groups": [ // 改动日历的分组配色,取第一个命中的
|
|
85
91
|
{ "id": "core", "label": "核心", "hue": 214, "sat": 58, "match": ["src/core/**"] },
|
|
86
92
|
{ "id": "web", "label": "界面", "hue": 152, "sat": 46, "match": ["src/web/**"] },
|
|
87
|
-
{ "id": "
|
|
93
|
+
{ "id": "rest", "label": "其余", "hue": 32, "sat": 62, "match": ["**"] }
|
|
88
94
|
],
|
|
89
|
-
//
|
|
90
|
-
"categories": [
|
|
95
|
+
"categories": [ // 行数视图的分类配色与图例
|
|
91
96
|
{ "id": "core", "label": "核心代码", "hue": 214, "sat": 58, "match": ["src/core/**"] },
|
|
92
97
|
{ "id": "test", "label": "测试", "hue": 152, "sat": 46, "defaultOn": false, "match": ["**/*.test.ts"] },
|
|
93
98
|
{ "id": "app", "label": "其他代码", "hue": 220, "sat": 20 }
|
|
94
99
|
],
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
100
|
+
"ignore": ["data/**", "**/*.snap"] // 额外跳过的路径
|
|
101
|
+
},
|
|
102
|
+
]
|
|
99
103
|
}
|
|
100
104
|
```
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
| 字段 | 作用 |
|
|
107
|
+
| ----------------------------------------------- | ----------------------------------------------------------------- |
|
|
108
|
+
| `profiles[*].id` | 预设名,`--profile` 指定名称。同一份配置里不能重复。 |
|
|
109
|
+
| `profiles[*].label` | 启动日志里显示的预设名。不写时用各分组名拼出来。 |
|
|
110
|
+
| `profiles[*].{groups/categories}[*].id` | 标识。同一份列表里不能重复,也不能叫 `all`。 |
|
|
111
|
+
| `profiles[*].{groups/categories}[*].label` | 页面上显示的名字。你写的名字会原样显示。 |
|
|
112
|
+
| `profiles[*].{groups/categories}[*].match` | 路径 glob。文件命中哪一条,就归入那一项。分组必填,分类可以省略。 |
|
|
113
|
+
| `profiles[*].{groups/categories}[*].hue`、`sat` | 这一项的颜色,用 HSL 表示。默认 214 和 50。 |
|
|
114
|
+
| `profiles[*].categories[*].defaultOn` | 设为 `false` 时,这个分类在图例里默认关闭。读者可以自己打开。 |
|
|
115
|
+
| `profiles[*].ignore` | 额外跳过的路径。它会和 `--exclude` 合并。 |
|
|
116
|
+
|
|
117
|
+
说明:
|
|
118
|
+
|
|
119
|
+
- glob 的路径相对仓库根目录。`**` 跨目录。`*` 和 `?` 只在单段里匹配。`{a,b}` 表示任选一个。
|
|
120
|
+
- 不含 `/` 的模式匹配任意层级的同名项。`/foo` 从仓库根算起。`foo/` 表示目录及其全部内容。
|
|
121
|
+
- 配置里可以写 `//`、`/* */` 注释和尾随逗号。
|
|
122
|
+
- 不写 `categories` 时,codelens 用内置的六类。它们是应用代码、测试、脚本、文档、配置、生成代码。其中「文档」「配置」「生成代码」默认关闭。
|
|
123
|
+
- 配置里没有的预设名会回退到内置预设。两边都没有就报错。
|
|
113
124
|
|
|
114
125
|
## 统计规则
|
|
115
126
|
|
|
116
|
-
-
|
|
117
|
-
- 始终跳过 `node_modules`、`dist`、`build`、`coverage`、`.venv`、`__pycache__`、`target`
|
|
118
|
-
- `--exclude`
|
|
119
|
-
- 二进制文件、超过 3MB
|
|
120
|
-
- 改动日历按提交时间(committer date
|
|
121
|
-
-
|
|
122
|
-
-
|
|
127
|
+
- 默认按仓库的 `.gitignore` 过滤。目录不是 git 仓库时,codelens 改用等价的忽略规则。`--no-gitignore` 可以关掉这个过滤。
|
|
128
|
+
- codelens 始终跳过 `node_modules`、`dist`、`build`、`coverage`、`.venv`、`__pycache__`、`target` 这类依赖和构建目录。
|
|
129
|
+
- `--exclude` 和配置里的 `ignore` 对两个视图都生效。被排除的目录不统计行数,也不出现在改动日历里。
|
|
130
|
+
- 二进制文件、超过 3MB 的文件、空文件都不统计。读不出的文件会被跳过,数量写在启动日志里。行数按物理行数算,文件末尾的换行不算一行。
|
|
131
|
+
- 改动日历按提交时间(committer date)把提交放到当天。`--days` 用的是同一个时间。改动行数是新增行数加删除行数。
|
|
132
|
+
- 合并提交、空提交、只改权限的提交、只改二进制的提交都没有行数。它们仍然出现在提交列表里,也计入提交数。
|
|
133
|
+
- 在仓库的子目录里运行时,两个视图只统计这个子目录,路径也相对它计算。
|
|
123
134
|
|
|
124
135
|
## 已知限制
|
|
125
136
|
|
|
126
137
|
- 改动日历依赖 git:没有提交或没有 git 时日历为空,行数视图仍可用。
|
|
127
|
-
- 行数不代表代码复杂度。
|
|
128
138
|
- 树形图一次最多绘制 6000 个方块,超出部分不显示。
|
|
129
|
-
-
|
|
139
|
+
- 行数不代表代码复杂度。
|
|
130
140
|
|
|
131
141
|
## 多语言
|
|
132
142
|
|
|
133
|
-
页面语言跟随浏览器,内置简体中文、English
|
|
143
|
+
页面语言跟随浏览器,内置简体中文、English、日本語、한국어,默认英文。地址后加 `?lang={langCode}` 可临时覆盖,例如 `?lang=en`。
|
|
134
144
|
|
|
135
145
|
## 开发
|
|
136
146
|
|
package/dist/cli/index.js
CHANGED
|
@@ -102,19 +102,23 @@ async function main() {
|
|
|
102
102
|
});
|
|
103
103
|
console.log(`codelens ${version()}`);
|
|
104
104
|
log('repo', root);
|
|
105
|
-
log('profile', `${profile.name} (${profile.label})
|
|
105
|
+
log('profile', `${profile.name} (${profile.label})`);
|
|
106
|
+
if (profile.configPath) {
|
|
107
|
+
log('config', profile.configPath);
|
|
108
|
+
}
|
|
106
109
|
const loc = await readLoc(root, profile, args.useGitignore);
|
|
107
110
|
const scanLabel = loc.scan.mode === 'git' ? 'git index, filtered by .gitignore' : 'directory walk';
|
|
108
|
-
log('files', `${f(loc.data.totals.files)} files
|
|
111
|
+
log('files', `${f(loc.data.totals.files)} files, ${f(loc.data.totals.lines)} lines (${scanLabel})`);
|
|
109
112
|
if (loc.data.skipped.unreadable > 0) {
|
|
110
113
|
log('warn', `${f(loc.data.skipped.unreadable)} files could not be read and were skipped`);
|
|
111
114
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
// 分组一行一个,规则长了也不会挤成一条
|
|
116
|
+
profile.groups.forEach((group, index) => {
|
|
117
|
+
log(index === 0 ? 'groups' : '', `${group.label}=${group.match.join(' ')}`);
|
|
118
|
+
});
|
|
115
119
|
const calendar = await readCalendar(root, profile, args.days);
|
|
116
120
|
log('calendar', calendar.range.min
|
|
117
|
-
? `${calendar.range.min} ~ ${calendar.range.max}
|
|
121
|
+
? `${calendar.range.min} ~ ${calendar.range.max} (${calendar.totals.days} days with commits)`
|
|
118
122
|
: 'no commits in this range');
|
|
119
123
|
if (args.dump) {
|
|
120
124
|
const outDir = resolve(args.dump);
|
package/dist/core/profile.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
//
|
|
3
|
-
// --profile
|
|
4
|
-
//
|
|
2
|
+
// 预设
|
|
3
|
+
// --profile 选择一份预设:内置的 all(不区分)与 web(常见前后端目录),
|
|
4
|
+
// 或在配置 codelens.config.json(也可用 --profile 直接指向 json 文件)里自定义。
|
|
5
|
+
// 配置的 profiles 是数组,每项带 id,它就是 --profile 后面写的名字。
|
|
5
6
|
// 用 groups 划分前后端等分组,用 categories 划分模块与文件类别。
|
|
6
7
|
// 内置项的 label 是英文,labelKey 供页面按语言覆盖;用户配置的 label 原样使用。
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
@@ -78,9 +79,9 @@ export const DEFAULT_CATEGORIES = [
|
|
|
78
79
|
{ id: 'app', label: 'Application code', labelKey: 'category.app', hue: 214, sat: 58 },
|
|
79
80
|
];
|
|
80
81
|
// ---------------------------------------------------------------------------
|
|
81
|
-
//
|
|
82
|
+
// 内置预设
|
|
82
83
|
// ---------------------------------------------------------------------------
|
|
83
|
-
/**
|
|
84
|
+
/** 未配置的兜底预设:不做任何分组,整个仓库一起统计。 */
|
|
84
85
|
const PROFILE_ALL = { label: 'All', labelKey: 'profile.all' };
|
|
85
86
|
/** 常见前后端目录布局:前端目录归前端,其余归后端。 */
|
|
86
87
|
const PROFILE_WEB = {
|
|
@@ -208,6 +209,25 @@ function parseEntry(value, where) {
|
|
|
208
209
|
}
|
|
209
210
|
return entry;
|
|
210
211
|
}
|
|
212
|
+
function parseProfiles(value, where) {
|
|
213
|
+
if (value === undefined) {
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
216
|
+
if (!Array.isArray(value)) {
|
|
217
|
+
fail(where, 'must be an array');
|
|
218
|
+
}
|
|
219
|
+
const seen = new Set();
|
|
220
|
+
return value.map((item, i) => {
|
|
221
|
+
const at = `${where}[${i}]`;
|
|
222
|
+
const obj = asObject(item, at);
|
|
223
|
+
const id = asString(obj.id, `${at}.id`);
|
|
224
|
+
if (seen.has(id)) {
|
|
225
|
+
fail(where, `contain a duplicate id: ${id}`);
|
|
226
|
+
}
|
|
227
|
+
seen.add(id);
|
|
228
|
+
return { id, entry: parseEntry(obj, at) };
|
|
229
|
+
});
|
|
230
|
+
}
|
|
211
231
|
function readJson(path) {
|
|
212
232
|
let text;
|
|
213
233
|
try {
|
|
@@ -279,10 +299,23 @@ function stripJsonc(text) {
|
|
|
279
299
|
}
|
|
280
300
|
return out;
|
|
281
301
|
}
|
|
302
|
+
/** 同一份列表里的 id 必须唯一,也不能占用保留名。 */
|
|
303
|
+
function checkIds(defs, where) {
|
|
304
|
+
const seen = new Set();
|
|
305
|
+
for (const def of defs) {
|
|
306
|
+
if (seen.has(def.id)) {
|
|
307
|
+
fail(where, `contain a duplicate id: ${def.id}`);
|
|
308
|
+
}
|
|
309
|
+
if (RESERVED_IDS.has(def.id)) {
|
|
310
|
+
fail(where, `must not use the reserved id: ${def.id}`);
|
|
311
|
+
}
|
|
312
|
+
seen.add(def.id);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
282
315
|
/**
|
|
283
|
-
*
|
|
284
|
-
* 1. 名字指向 json
|
|
285
|
-
* 2.
|
|
316
|
+
* 按名字解析预设:
|
|
317
|
+
* 1. 名字指向 json 文件时直接当预设用;
|
|
318
|
+
* 2. 否则先找配置的 profiles 里 id 同名的一项,再找内置预设。
|
|
286
319
|
*/
|
|
287
320
|
export function loadProfile(opts) {
|
|
288
321
|
const { root, name } = opts;
|
|
@@ -298,13 +331,14 @@ export function loadProfile(opts) {
|
|
|
298
331
|
}
|
|
299
332
|
else if (opts.configPath || exists(configPath)) {
|
|
300
333
|
const file = asObject(readJson(configPath), configPath);
|
|
301
|
-
const profiles =
|
|
302
|
-
|
|
303
|
-
|
|
334
|
+
const profiles = parseProfiles(file.profiles, `profiles of ${configPath}`);
|
|
335
|
+
const found = profiles.find((item) => item.id === name);
|
|
336
|
+
if (found) {
|
|
337
|
+
entry = found.entry;
|
|
304
338
|
usedConfig = configPath;
|
|
305
339
|
}
|
|
306
340
|
else if (hasOwn(BUILTIN_PROFILES, name)) {
|
|
307
|
-
//
|
|
341
|
+
// 配置里没有这个预设就回退内置预设:仓库里放了一份自定义配置,
|
|
308
342
|
// 不该让默认的 `codelens` / `--profile web` 直接跑不起来
|
|
309
343
|
entry = BUILTIN_PROFILES[name];
|
|
310
344
|
}
|
|
@@ -316,35 +350,24 @@ export function loadProfile(opts) {
|
|
|
316
350
|
entry = BUILTIN_PROFILES[name];
|
|
317
351
|
}
|
|
318
352
|
else {
|
|
319
|
-
throw new Error(`no profile named ${name}; available: ${names(
|
|
353
|
+
throw new Error(`no profile named ${name}; available: ${names([], BUILTIN_PROFILES)}`);
|
|
320
354
|
}
|
|
321
355
|
const groups = entry.groups ?? [];
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
if (RESERVED_IDS.has(group.id)) {
|
|
328
|
-
fail(`groups of ${usedConfig ?? name}`, `must not use the reserved id: ${group.id}`);
|
|
329
|
-
}
|
|
330
|
-
ids.add(group.id);
|
|
331
|
-
}
|
|
332
|
-
for (const category of entry.categories ?? []) {
|
|
333
|
-
if (RESERVED_IDS.has(category.id)) {
|
|
334
|
-
fail(`categories of ${usedConfig ?? name}`, `must not use the reserved id: ${category.id}`);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
356
|
+
const categories = entry.categories ?? DEFAULT_CATEGORIES;
|
|
357
|
+
// 分组与分类各用一套 id:一个给日历配色,一个给树形图配色,两边重名不算冲突
|
|
358
|
+
checkIds(groups, `groups of ${usedConfig ?? name}`);
|
|
359
|
+
checkIds(categories, `categories of ${usedConfig ?? name}`);
|
|
337
360
|
return {
|
|
338
361
|
name,
|
|
339
362
|
label: entry.label ?? (groups.length > 0 ? groups.map((group) => group.label).join(' / ') : 'All'),
|
|
340
363
|
groups,
|
|
341
|
-
categories
|
|
364
|
+
categories,
|
|
342
365
|
ignore: [...(entry.ignore ?? []), ...opts.exclude],
|
|
343
366
|
configPath: usedConfig,
|
|
344
367
|
};
|
|
345
368
|
}
|
|
346
369
|
function names(profiles, builtin) {
|
|
347
|
-
return [...
|
|
370
|
+
return [...profiles.map((item) => item.id), ...Object.keys(builtin)].join(', ');
|
|
348
371
|
}
|
|
349
372
|
function isProfileFile(name) {
|
|
350
373
|
return name.endsWith('.json') || name.includes('/') || name.includes('\\');
|
|
@@ -357,7 +380,7 @@ function exists(path) {
|
|
|
357
380
|
return false;
|
|
358
381
|
}
|
|
359
382
|
}
|
|
360
|
-
/**
|
|
383
|
+
/** 供帮助信息使用:列出内置预设。 */
|
|
361
384
|
export function builtinProfileNames() {
|
|
362
385
|
return Object.keys(BUILTIN_PROFILES);
|
|
363
386
|
}
|