@coffic/cosy-ui 0.5.8 → 0.5.12

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.
@@ -5,135 +5,110 @@ const SHOW_TIMESTAMP = false;
5
5
 
6
6
  // ANSI 颜色代码
7
7
  const colors = {
8
- reset: '\x1b[0m',
9
- debug: '\x1b[36m', // 青色
10
- info: '\x1b[32m', // 绿色
11
- warn: '\x1b[33m', // 黄色
12
- error: '\x1b[31m', // 红色
13
- gray: '\x1b[90m', // 灰色用于时间戳
8
+ reset: '\x1b[0m',
9
+ debug: '\x1b[36m', // 青色
10
+ info: '\x1b[32m', // 绿色
11
+ warn: '\x1b[33m', // 黄色
12
+ error: '\x1b[31m', // 红色
13
+ gray: '\x1b[90m', // 灰色用于时间戳
14
14
  };
15
15
 
16
16
  class Logger {
17
- private getCallerInfo(): string {
18
- const error = new Error();
19
- const stackLines = error.stack?.split('\n') || [];
20
-
21
- // 从第3行开始查找,跳过不是来自logger.ts的第一个调用
22
- let targetLine = '';
23
- for (let i = 3; i < stackLines.length; i++) {
24
- const line = stackLines[i];
25
- if (!line.includes('logger.ts')) {
26
- targetLine = line;
27
- break;
28
- }
29
- }
30
-
31
- if (!targetLine) return '';
32
-
33
- // 匹配文件路径和行号
34
- const match = targetLine.match(/\((.+):(\d+):\d+\)/) || targetLine.match(/at (.+):(\d+):\d+/);
35
- if (!match) return '';
36
-
37
- const [_, filePath, line] = match;
38
- return `${filePath}:${line}`;
39
- }
40
-
41
- private formatArray(arr: any[]): string {
42
- const MAX_LINES = 30;
43
- const MAX_LENGTH = 100;
44
-
45
- const truncateString = (str: string): string => {
46
- return str.length > MAX_LENGTH ? str.slice(0, MAX_LENGTH) + '...' : str;
47
- };
48
-
49
- const truncateObject = (obj: any): any => {
50
- if (typeof obj !== 'object' || obj === null) {
51
- return typeof obj === 'string' ? truncateString(obj) : obj;
52
- }
53
-
54
- const result: any = Array.isArray(obj) ? [] : {};
55
- for (const [key, value] of Object.entries(obj)) {
56
- result[key] = typeof value === 'string' ? truncateString(value)
57
- : typeof value === 'object' ? truncateObject(value)
58
- : value;
59
- }
60
- return result;
61
- };
62
-
63
- const items = arr.slice(0, MAX_LINES).map(item => {
64
- const truncatedItem = truncateObject(item);
65
- // 使用2个空格缩进,并在每行前添加 " • "
66
- const jsonString = JSON.stringify(truncatedItem, null, 2)
67
- .split('\n')
68
- .map((line, index) => index === 0 ? ` • ${line}` : ` ${line}`)
69
- .join('\n');
70
- return jsonString;
71
- });
72
-
73
- let output = items.join('\n');
74
- if (arr.length > MAX_LINES) {
75
- const remainingCount = arr.length - MAX_LINES;
76
- output += `\n ⋮ ... and ${remainingCount} more items`;
77
- }
78
-
79
- return output;
80
- }
81
-
82
- private log(level: LogLevel, message: string | object | any[]) {
83
- const caller = this.getCallerInfo();
84
- // 使用本地时间,并格式化为 HH:mm:ss 格式
85
- const timestamp = new Date().toLocaleTimeString('zh-CN', {
86
- hour12: false,
87
- hour: '2-digit',
88
- minute: '2-digit',
89
- second: '2-digit'
90
- });
91
-
92
- const formattedMessage = Array.isArray(message)
93
- ? this.formatArray(message)
94
- : typeof message === 'object'
95
- ? JSON.stringify(message, null, 2)
96
- : message;
97
-
98
- const timestampPart = SHOW_TIMESTAMP
99
- ? `${colors.gray}${timestamp}${colors.reset} `
100
- : '';
101
-
102
- const emoji = {
103
- debug: '🔍',
104
- info: '🐳',
105
- warn: '🚨',
106
- error: '❌'
107
- }[level];
108
-
109
- // eslint-disable-next-line no-console
110
- console.log(
111
- timestampPart +
112
- `${colors[level]}${emoji} ${level.toUpperCase()}${colors.reset} ` +
113
- `${colors.gray}:${colors.reset} ` +
114
- `${colors[level]}${formattedMessage}${colors.reset}`
115
- );
116
- }
117
-
118
- debug(message: string | object) {
119
- this.log('debug', message);
120
- }
121
-
122
- info(message: string | object) {
123
- this.log('info', message);
124
- }
125
-
126
- warn(message: string | object) {
127
- this.log('warn', message);
128
- }
129
-
130
- error(message: string | object) {
131
- this.log('error', message);
132
- }
133
-
134
- array(title: string, arr: any[]) {
135
- this.log('info', title + '\n' + this.formatArray(arr));
136
- }
17
+ private formatArray(arr: any[]): string {
18
+ const MAX_LINES = 30;
19
+ const MAX_LENGTH = 100;
20
+
21
+ const truncateString = (str: string): string => {
22
+ return str.length > MAX_LENGTH ? str.slice(0, MAX_LENGTH) + '...' : str;
23
+ };
24
+
25
+ const truncateObject = (obj: any): any => {
26
+ if (typeof obj !== 'object' || obj === null) {
27
+ return typeof obj === 'string' ? truncateString(obj) : obj;
28
+ }
29
+
30
+ const result: any = Array.isArray(obj) ? [] : {};
31
+ for (const [key, value] of Object.entries(obj)) {
32
+ result[key] =
33
+ typeof value === 'string'
34
+ ? truncateString(value)
35
+ : typeof value === 'object'
36
+ ? truncateObject(value)
37
+ : value;
38
+ }
39
+ return result;
40
+ };
41
+
42
+ const items = arr.slice(0, MAX_LINES).map((item) => {
43
+ const truncatedItem = truncateObject(item);
44
+ // 使用2个空格缩进,并在每行前添加 " • "
45
+ const jsonString = JSON.stringify(truncatedItem, null, 2)
46
+ .split('\n')
47
+ .map((line, index) => (index === 0 ? ` • ${line}` : ` ${line}`))
48
+ .join('\n');
49
+ return jsonString;
50
+ });
51
+
52
+ let output = items.join('\n');
53
+ if (arr.length > MAX_LINES) {
54
+ const remainingCount = arr.length - MAX_LINES;
55
+ output += `\n ⋮ ... and ${remainingCount} more items`;
56
+ }
57
+
58
+ return output;
59
+ }
60
+
61
+ private log(level: LogLevel, message: string | object | any[]) {
62
+ // 使用本地时间,并格式化为 HH:mm:ss 格式
63
+ const timestamp = new Date().toLocaleTimeString('zh-CN', {
64
+ hour12: false,
65
+ hour: '2-digit',
66
+ minute: '2-digit',
67
+ second: '2-digit',
68
+ });
69
+
70
+ const formattedMessage = Array.isArray(message)
71
+ ? this.formatArray(message)
72
+ : typeof message === 'object'
73
+ ? JSON.stringify(message, null, 2)
74
+ : message;
75
+
76
+ const timestampPart = SHOW_TIMESTAMP ? `${colors.gray}${timestamp}${colors.reset} ` : '';
77
+
78
+ const emoji = {
79
+ debug: '🔍',
80
+ info: '🐳',
81
+ warn: '🚨',
82
+ error: '❌',
83
+ }[level];
84
+
85
+ console.log(
86
+ timestampPart +
87
+ `${colors[level]}${emoji} ${level.toUpperCase()}${colors.reset} ` +
88
+ `${colors.gray}:${colors.reset} ` +
89
+ `${colors[level]}${formattedMessage}${colors.reset}`
90
+ );
91
+ }
92
+
93
+ debug(message: string | object) {
94
+ this.log('debug', message);
95
+ }
96
+
97
+ info(message: string | object) {
98
+ this.log('info', message);
99
+ }
100
+
101
+ warn(message: string | object) {
102
+ this.log('warn', message);
103
+ }
104
+
105
+ error(message: string | object) {
106
+ this.log('error', message);
107
+ }
108
+
109
+ array(title: string, arr: any[]) {
110
+ this.log('info', title + '\n' + this.formatArray(arr));
111
+ }
137
112
  }
138
113
 
139
114
  export const logger = new Logger();
@@ -63,7 +63,7 @@ function detectPlatform(url: string): [string, PlatformConfig] | null {
63
63
  }
64
64
  }
65
65
  } catch (e) {
66
- console.error('Invalid URL:', url);
66
+ console.error('Invalid URL:', url, e);
67
67
  }
68
68
  return null;
69
69
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffic/cosy-ui",
3
- "version": "0.5.8",
3
+ "version": "0.5.12",
4
4
  "description": "An astro component library",
5
5
  "author": {
6
6
  "name": "nookery",
@@ -35,7 +35,9 @@
35
35
  "preview:docs": "astro preview --host 0.0.0.0 --port 4330 --outDir dist-docs",
36
36
  "preview": "npm run preview:docs",
37
37
  "build": "vite build && npm run build:docs && tsx scripts/post-build.ts",
38
- "build:docs": "astro build"
38
+ "build:docs": "astro build",
39
+ "check": "astro check",
40
+ "lint": "eslint ."
39
41
  },
40
42
  "type": "module",
41
43
  "peerDependencies": {
@@ -47,32 +49,36 @@
47
49
  },
48
50
  "devDependencies": {
49
51
  "@astrojs/check": "^0.9.4",
50
- "@astrojs/mdx": "^4.2.0",
52
+ "@astrojs/mdx": "^4.2.6",
51
53
  "@astrojs/ts-plugin": "^1.10.4",
52
- "@astrojs/vue": "^5.0.8",
54
+ "@astrojs/vue": "^5.0.13",
55
+ "@eslint/js": "^9.26.0",
53
56
  "@tailwindcss/typography": "^0.5.16",
54
- "@tailwindcss/vite": "^4.1.4",
55
- "@types/chai": "^5.2.0",
57
+ "@tailwindcss/vite": "^4.1.5",
58
+ "@types/chai": "^5.2.2",
56
59
  "@types/eslint": "^9.6.1",
57
60
  "@types/fs-extra": "^11.0.4",
58
61
  "@types/mocha": "^10.0.10",
59
- "@types/node": "^22.13.10",
60
- "@types/react": "^19.0.10",
61
- "@typescript-eslint/parser": "^8.26.1",
62
- "astro": "^5.5.2",
62
+ "@types/node": "^22.15.16",
63
+ "@types/react": "^19.1.3",
64
+ "@typescript-eslint/parser": "^8.32.0",
65
+ "astro": "^5.7.11",
63
66
  "chai": "^4.5.0",
64
- "daisyui": "^5.0.4",
65
- "eslint": "^8.57.1",
67
+ "daisyui": "^5.0.35",
68
+ "eslint": "^9.26.0",
66
69
  "eslint-plugin-astro": "^1.3.1",
70
+ "globals": "^16.1.0",
67
71
  "mocha": "^10.8.2",
68
72
  "prettier": "^3.5.3",
69
73
  "prettier-plugin-astro": "^0.14.1",
70
74
  "rollup-plugin-copy": "^3.5.0",
71
- "sharp": "^0.33.2",
72
- "tailwindcss": "^4.1.4",
73
- "tsx": "^4.19.3",
74
- "typescript": "^5.8.2",
75
- "vite": "^6.2.2",
76
- "vue": "^3.5.13"
75
+ "sharp": "^0.33.5",
76
+ "tailwindcss": "^4.1.5",
77
+ "tsx": "^4.19.4",
78
+ "typescript": "^5.8.3",
79
+ "typescript-eslint": "^8.32.0",
80
+ "vite": "^6.3.5",
81
+ "vue": "^3.5.13",
82
+ "vue-eslint-parser": "^10.1.3"
77
83
  }
78
84
  }
@@ -1,164 +0,0 @@
1
- import { render, type RenderResult, type CollectionEntry, type DataEntryMap } from "astro:content";
2
- import { SidebarItemEntity, type SidebarProvider } from "../entities/SidebarItem";
3
- import { logger } from "../utils/logger";
4
-
5
- /**
6
- * 文档基类,提供所有文档类型共享的基本功能
7
- */
8
- export abstract class BaseDoc<Collection extends keyof DataEntryMap, T extends CollectionEntry<Collection>> implements SidebarProvider {
9
- protected entry: T;
10
-
11
- constructor(entry: T) {
12
- this.entry = entry;
13
- }
14
-
15
- /**
16
- * 获取文档ID
17
- */
18
- getId(): string {
19
- return this.entry.id;
20
- }
21
-
22
- /**
23
- * 获取文档标题
24
- */
25
- getTitle(): string {
26
- return this.entry.data.title as string;
27
- }
28
-
29
- /**
30
- * 获取文档语言
31
- */
32
- getLang(): string {
33
- return this.entry.id.split('/')[0];
34
- }
35
-
36
- /**
37
- * 获取文档slug
38
- */
39
- getSlug(): string {
40
- return this.getId().split('/').slice(1).join('/');
41
- }
42
-
43
- /**
44
- * 获取文档描述
45
- */
46
- getDescription(): string {
47
- return this.entry.data.description as string;
48
- }
49
-
50
- /**
51
- * 获取文档链接
52
- * 每个子类必须实现此方法以提供正确的链接
53
- */
54
- abstract getLink(): string;
55
-
56
- /**
57
- * 渲染文档内容
58
- */
59
- async render(): Promise<RenderResult> {
60
- return await render(this.entry);
61
- }
62
-
63
- /**
64
- * 获取文档的层级深度
65
- * 例如:对于 ID 为 "zh-cn/blog/typescript" 的文档,深度为3
66
- */
67
- getLevel(): number {
68
- return this.entry.id.split('/').length;
69
- }
70
-
71
- /**
72
- * 转换为侧边栏项目
73
- * 基本实现,只包含当前文档
74
- */
75
- async toSidebarItem(): Promise<SidebarItemEntity> {
76
- return new SidebarItemEntity({
77
- text: this.getTitle(),
78
- link: this.getLink(),
79
- });
80
- }
81
- }
82
-
83
- /**
84
- * 层级文档基类,为有层级结构的文档类型提供额外功能
85
- * 例如课程等有父子关系的文档
86
- */
87
- export abstract class HierarchicalDoc<Collection extends keyof DataEntryMap, T extends CollectionEntry<Collection>> extends BaseDoc<Collection, T> {
88
- /**
89
- * 获取父文档ID
90
- * 例如:对于 ID 为 "zh-cn/blog/typescript" 的文档,父ID为 "zh-cn/blog"
91
- *
92
- * @returns 父文档ID,如果没有父文档则返回null
93
- */
94
- getParentId(): string | null {
95
- const parts = this.entry.id.split('/');
96
- return parts.length > 1 ? parts.slice(0, -1).join('/') : null;
97
- }
98
-
99
- /**
100
- * 获取顶级文档的ID
101
- * 例如:对于 ID 为 "zh-cn/blog/typescript" 的文档,顶级ID为 "zh-cn/blog"
102
- *
103
- * 默认实现假设顶级ID是前两部分
104
- * 子类可以根据需要覆盖此方法
105
- */
106
- async getTopDocId(): Promise<string> {
107
- const id = this.entry.id;
108
- const parts = id.split('/');
109
- return parts[0] + '/' + parts[1];
110
- }
111
-
112
- /**
113
- * 获取顶级文档
114
- * 子类应该实现此方法以提供正确的顶级文档
115
- */
116
- abstract getTopDoc(): Promise<HierarchicalDoc<Collection, T> | null>;
117
-
118
- /**
119
- * 获取子文档
120
- * 子类应该实现此方法以提供正确的子文档列表
121
- */
122
- abstract getChildren(): Promise<HierarchicalDoc<Collection, T>[]>;
123
-
124
- /**
125
- * 转换为侧边栏项目
126
- * 如果文档有子文档,会包含子文档的侧边栏项目
127
- */
128
- override async toSidebarItem(): Promise<SidebarItemEntity> {
129
- const debug = false;
130
-
131
- const children = await this.getChildren();
132
- const childItems = await Promise.all(children.map(child => child.toSidebarItem()));
133
-
134
- if (debug) {
135
- logger.info(`${this.entry.id} 的侧边栏项目`);
136
- // eslint-disable-next-line no-console
137
- console.log(childItems);
138
- }
139
-
140
- return new SidebarItemEntity({
141
- text: this.getTitle(),
142
- items: childItems,
143
- link: this.getLink(),
144
- });
145
- }
146
-
147
- /**
148
- * 获取顶级侧边栏项目
149
- * 如果有顶级文档,返回顶级文档的侧边栏项目
150
- * 否则返回当前文档的侧边栏项目
151
- */
152
- async getTopSidebarItem(): Promise<SidebarItemEntity> {
153
- const topDoc = await this.getTopDoc();
154
- if (topDoc) {
155
- return await topDoc.toSidebarItem();
156
- }
157
-
158
- return new SidebarItemEntity({
159
- text: this.getTitle(),
160
- items: [],
161
- link: this.getLink(),
162
- });
163
- }
164
- }