@incremark/shared 0.2.3
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/LICENSE +22 -0
- package/README.md +44 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +119 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 wangyishuai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# @incremark/shared
|
|
2
|
+
|
|
3
|
+
Incremark 共享工具包 - Vue 和 React 共享的逻辑和工具函数。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- **HTML 处理**:处理 HTML 节点配对,自动补全缺失的结束标签
|
|
8
|
+
- **文本处理**:处理带 chunks 的文本节点,支持渐入动画
|
|
9
|
+
- **类型定义**:共享的类型定义
|
|
10
|
+
|
|
11
|
+
## 使用
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import {
|
|
15
|
+
type TextNodeWithChunks,
|
|
16
|
+
hasChunks,
|
|
17
|
+
getStableText,
|
|
18
|
+
isHtmlNode,
|
|
19
|
+
processHtmlNodes
|
|
20
|
+
} from '@incremark/shared'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## API
|
|
24
|
+
|
|
25
|
+
### `processHtmlNodes(nodes: PhrasingContent[]): PhrasingContent[]`
|
|
26
|
+
|
|
27
|
+
处理 HTML 节点数组,配对开始和结束标签。如果遇到开始标签但没有对应的结束标签,会自动补上结束标签。
|
|
28
|
+
|
|
29
|
+
### `hasChunks(node: PhrasingContent): node is TextNodeWithChunks`
|
|
30
|
+
|
|
31
|
+
类型守卫,检查是否是带 chunks 的文本节点。
|
|
32
|
+
|
|
33
|
+
### `getStableText(node: TextNodeWithChunks): string`
|
|
34
|
+
|
|
35
|
+
获取文本节点的稳定部分(不需要动画的部分)。
|
|
36
|
+
|
|
37
|
+
### `isHtmlNode(node: PhrasingContent): node is HTML`
|
|
38
|
+
|
|
39
|
+
类型守卫,检查是否是 HTML 节点。
|
|
40
|
+
|
|
41
|
+
### `extractTagName(html: string): HtmlTagInfo | null`
|
|
42
|
+
|
|
43
|
+
提取 HTML 标签名和相关信息。
|
|
44
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Text, PhrasingContent, HTML } from 'mdast';
|
|
2
|
+
|
|
3
|
+
interface TextChunk {
|
|
4
|
+
/** 文本内容 */
|
|
5
|
+
text: string;
|
|
6
|
+
/** 创建时间戳 */
|
|
7
|
+
createdAt: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 扩展的文本节点(支持 chunks)
|
|
11
|
+
*/
|
|
12
|
+
interface TextNodeWithChunks extends Text {
|
|
13
|
+
stableLength?: number;
|
|
14
|
+
chunks?: TextChunk[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* HTML 标签信息
|
|
19
|
+
*/
|
|
20
|
+
interface HtmlTagInfo {
|
|
21
|
+
tagName: string;
|
|
22
|
+
isClosing: boolean;
|
|
23
|
+
isSelfClosing: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 提取 HTML 标签名(支持自闭合标签)
|
|
27
|
+
*/
|
|
28
|
+
declare function extractTagName(html: string): HtmlTagInfo | null;
|
|
29
|
+
/**
|
|
30
|
+
* 类型守卫:检查是否是 HTML 节点
|
|
31
|
+
*/
|
|
32
|
+
declare function isHtmlNode(node: PhrasingContent): node is HTML;
|
|
33
|
+
/**
|
|
34
|
+
* HTML 包装节点:包含开始标签、内容节点、结束标签
|
|
35
|
+
* 这样可以在渲染时一起处理,避免空标签
|
|
36
|
+
*/
|
|
37
|
+
interface HtmlWrapperNode {
|
|
38
|
+
type: 'html-wrapper';
|
|
39
|
+
startTag: string;
|
|
40
|
+
content: PhrasingContent[];
|
|
41
|
+
endTag: string;
|
|
42
|
+
tagName: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 类型守卫:检查是否是 HTML 包装节点
|
|
46
|
+
*/
|
|
47
|
+
declare function isHtmlWrapperNode(node: PhrasingContent | HtmlWrapperNode): node is HtmlWrapperNode;
|
|
48
|
+
/**
|
|
49
|
+
* 处理 HTML 节点数组,将开始标签、中间内容、结束标签包装在一起
|
|
50
|
+
* 这样渲染时可以一起处理,避免空标签
|
|
51
|
+
*/
|
|
52
|
+
declare function processHtmlNodes(nodes: PhrasingContent[]): (PhrasingContent | HtmlWrapperNode)[];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 类型守卫:检查是否是带 chunks 的文本节点
|
|
56
|
+
*/
|
|
57
|
+
declare function hasChunks(node: PhrasingContent): node is TextNodeWithChunks;
|
|
58
|
+
/**
|
|
59
|
+
* 获取文本节点的稳定部分(不需要动画)
|
|
60
|
+
*/
|
|
61
|
+
declare function getStableText(node: TextNodeWithChunks): string;
|
|
62
|
+
|
|
63
|
+
export { type HtmlTagInfo, type HtmlWrapperNode, type TextNodeWithChunks, extractTagName, getStableText, hasChunks, isHtmlNode, isHtmlWrapperNode, processHtmlNodes };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// src/html.ts
|
|
2
|
+
function extractTagName(html) {
|
|
3
|
+
const match = html.match(/^<\/?([a-zA-Z][a-zA-Z0-9-]*)\s*\/?>?/);
|
|
4
|
+
if (!match) return null;
|
|
5
|
+
const isClosing = html.startsWith("</");
|
|
6
|
+
const isSelfClosing = html.endsWith("/>") || !!html.match(/^<[^>]+\/>$/);
|
|
7
|
+
return {
|
|
8
|
+
tagName: match[1].toLowerCase(),
|
|
9
|
+
isClosing,
|
|
10
|
+
isSelfClosing
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function isHtmlNode(node) {
|
|
14
|
+
return node.type === "html";
|
|
15
|
+
}
|
|
16
|
+
function isHtmlWrapperNode(node) {
|
|
17
|
+
return node.type === "html-wrapper";
|
|
18
|
+
}
|
|
19
|
+
function processHtmlNodes(nodes) {
|
|
20
|
+
const result = [];
|
|
21
|
+
let i = 0;
|
|
22
|
+
while (i < nodes.length) {
|
|
23
|
+
const node = nodes[i];
|
|
24
|
+
if (isHtmlNode(node)) {
|
|
25
|
+
const tagInfo = extractTagName(node.value);
|
|
26
|
+
if (tagInfo) {
|
|
27
|
+
if (tagInfo.isSelfClosing) {
|
|
28
|
+
result.push(node);
|
|
29
|
+
i++;
|
|
30
|
+
} else if (tagInfo.isClosing) {
|
|
31
|
+
i++;
|
|
32
|
+
continue;
|
|
33
|
+
} else {
|
|
34
|
+
const startTag = node.value;
|
|
35
|
+
const tagName = tagInfo.tagName;
|
|
36
|
+
const contentNodes = [];
|
|
37
|
+
let foundClosing = false;
|
|
38
|
+
let j = i + 1;
|
|
39
|
+
let depth = 1;
|
|
40
|
+
while (j < nodes.length && depth > 0) {
|
|
41
|
+
const nextNode = nodes[j];
|
|
42
|
+
if (isHtmlNode(nextNode)) {
|
|
43
|
+
const nextTagInfo = extractTagName(nextNode.value);
|
|
44
|
+
if (nextTagInfo) {
|
|
45
|
+
if (nextTagInfo.isClosing && nextTagInfo.tagName === tagName) {
|
|
46
|
+
depth--;
|
|
47
|
+
if (depth === 0) {
|
|
48
|
+
foundClosing = true;
|
|
49
|
+
const endTag = nextNode.value;
|
|
50
|
+
const wrapperNode = {
|
|
51
|
+
type: "html-wrapper",
|
|
52
|
+
startTag,
|
|
53
|
+
content: contentNodes,
|
|
54
|
+
endTag,
|
|
55
|
+
tagName
|
|
56
|
+
};
|
|
57
|
+
result.push(wrapperNode);
|
|
58
|
+
i = j + 1;
|
|
59
|
+
break;
|
|
60
|
+
} else {
|
|
61
|
+
contentNodes.push(nextNode);
|
|
62
|
+
j++;
|
|
63
|
+
}
|
|
64
|
+
} else if (!nextTagInfo.isClosing && nextTagInfo.tagName === tagName) {
|
|
65
|
+
depth++;
|
|
66
|
+
contentNodes.push(nextNode);
|
|
67
|
+
j++;
|
|
68
|
+
} else {
|
|
69
|
+
contentNodes.push(nextNode);
|
|
70
|
+
j++;
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
contentNodes.push(nextNode);
|
|
74
|
+
j++;
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
contentNodes.push(nextNode);
|
|
78
|
+
j++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!foundClosing) {
|
|
82
|
+
const endTag = `</${tagName}>`;
|
|
83
|
+
const wrapperNode = {
|
|
84
|
+
type: "html-wrapper",
|
|
85
|
+
startTag,
|
|
86
|
+
content: contentNodes,
|
|
87
|
+
endTag,
|
|
88
|
+
tagName
|
|
89
|
+
};
|
|
90
|
+
result.push(wrapperNode);
|
|
91
|
+
i = j;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
result.push(node);
|
|
96
|
+
i++;
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
result.push(node);
|
|
100
|
+
i++;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/text.ts
|
|
107
|
+
function hasChunks(node) {
|
|
108
|
+
return node.type === "text" && "chunks" in node && Array.isArray(node.chunks);
|
|
109
|
+
}
|
|
110
|
+
function getStableText(node) {
|
|
111
|
+
if (!node.chunks || node.chunks.length === 0) {
|
|
112
|
+
return node.value;
|
|
113
|
+
}
|
|
114
|
+
return node.value.slice(0, node.stableLength ?? 0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export { extractTagName, getStableText, hasChunks, isHtmlNode, isHtmlWrapperNode, processHtmlNodes };
|
|
118
|
+
//# sourceMappingURL=index.js.map
|
|
119
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/html.ts","../src/text.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { PhrasingContent, HTML } from 'mdast'\n\n/**\n * HTML 标签信息\n */\nexport interface HtmlTagInfo {\n tagName: string\n isClosing: boolean\n isSelfClosing: boolean\n}\n\n/**\n * 提取 HTML 标签名(支持自闭合标签)\n */\nexport function extractTagName(html: string): HtmlTagInfo | null {\n const match = html.match(/^<\\/?([a-zA-Z][a-zA-Z0-9-]*)\\s*\\/?>?/)\n if (!match) return null\n \n const isClosing = html.startsWith('</')\n const isSelfClosing = html.endsWith('/>') || !!html.match(/^<[^>]+\\/>$/)\n return {\n tagName: match[1].toLowerCase(),\n isClosing,\n isSelfClosing\n }\n}\n\n/**\n * 类型守卫:检查是否是 HTML 节点\n */\nexport function isHtmlNode(node: PhrasingContent): node is HTML {\n return node.type === 'html'\n}\n\n/**\n * HTML 包装节点:包含开始标签、内容节点、结束标签\n * 这样可以在渲染时一起处理,避免空标签\n */\nexport interface HtmlWrapperNode {\n type: 'html-wrapper'\n startTag: string\n content: PhrasingContent[]\n endTag: string\n tagName: string\n}\n\n/**\n * 类型守卫:检查是否是 HTML 包装节点\n */\nexport function isHtmlWrapperNode(node: PhrasingContent | HtmlWrapperNode): node is HtmlWrapperNode {\n return (node as HtmlWrapperNode).type === 'html-wrapper'\n}\n\n/**\n * 处理 HTML 节点数组,将开始标签、中间内容、结束标签包装在一起\n * 这样渲染时可以一起处理,避免空标签\n */\nexport function processHtmlNodes(nodes: PhrasingContent[]): (PhrasingContent | HtmlWrapperNode)[] {\n const result: (PhrasingContent | HtmlWrapperNode)[] = []\n let i = 0\n \n while (i < nodes.length) {\n const node = nodes[i]\n \n if (isHtmlNode(node)) {\n const tagInfo = extractTagName(node.value)\n \n if (tagInfo) {\n if (tagInfo.isSelfClosing) {\n // 自闭合标签,直接添加\n result.push(node)\n i++\n } else if (tagInfo.isClosing) {\n // 结束标签,如果前面没有匹配的开始标签,跳过(可能是之前补上的)\n // 否则应该已经被包装处理了,这里不应该单独出现\n i++\n continue\n } else {\n // 开始标签:收集后续内容直到找到对应的结束标签\n const startTag = node.value\n const tagName = tagInfo.tagName\n const contentNodes: PhrasingContent[] = []\n let foundClosing = false\n let j = i + 1\n let depth = 1 // 嵌套深度\n \n // 收集开始标签和结束标签之间的所有节点\n while (j < nodes.length && depth > 0) {\n const nextNode = nodes[j]\n \n if (isHtmlNode(nextNode)) {\n const nextTagInfo = extractTagName(nextNode.value)\n if (nextTagInfo) {\n if (nextTagInfo.isClosing && nextTagInfo.tagName === tagName) {\n // 找到匹配的结束标签\n depth--\n if (depth === 0) {\n foundClosing = true\n const endTag = nextNode.value\n \n // 创建包装节点\n const wrapperNode: HtmlWrapperNode = {\n type: 'html-wrapper',\n startTag,\n content: contentNodes,\n endTag,\n tagName\n }\n \n result.push(wrapperNode)\n i = j + 1 // 跳过已处理的所有节点\n break\n } else {\n // 嵌套的结束标签,收集到内容中\n contentNodes.push(nextNode)\n j++\n }\n } else if (!nextTagInfo.isClosing && nextTagInfo.tagName === tagName) {\n // 嵌套的同名开始标签\n depth++\n contentNodes.push(nextNode)\n j++\n } else {\n // 其他 HTML 节点,收集到内容中\n contentNodes.push(nextNode)\n j++\n }\n } else {\n // 无法解析的 HTML,收集到内容中\n contentNodes.push(nextNode)\n j++\n }\n } else {\n // 非 HTML 节点(文本等),收集到内容中\n contentNodes.push(nextNode)\n j++\n }\n }\n \n if (!foundClosing) {\n // 没有找到结束标签,补上一个\n const endTag = `</${tagName}>`\n \n const wrapperNode: HtmlWrapperNode = {\n type: 'html-wrapper',\n startTag,\n content: contentNodes,\n endTag,\n tagName\n }\n \n result.push(wrapperNode)\n i = j // 跳过已处理的所有节点\n }\n }\n } else {\n // 无法解析的 HTML,直接添加\n result.push(node)\n i++\n }\n } else {\n // 非 HTML 节点,直接添加\n result.push(node)\n i++\n }\n }\n \n return result\n}\n\n","import type { PhrasingContent, Text } from 'mdast'\nimport type { TextNodeWithChunks } from './types'\n\n/**\n * 类型守卫:检查是否是带 chunks 的文本节点\n */\nexport function hasChunks(node: PhrasingContent): node is TextNodeWithChunks {\n return node.type === 'text' && 'chunks' in node && Array.isArray((node as TextNodeWithChunks).chunks)\n}\n\n/**\n * 获取文本节点的稳定部分(不需要动画)\n */\nexport function getStableText(node: TextNodeWithChunks): string {\n if (!node.chunks || node.chunks.length === 0) {\n return (node as Text).value\n }\n return (node as Text).value.slice(0, node.stableLength ?? 0)\n}\n\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@incremark/shared",
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"description": "Incremark 共享工具包 - Vue 和 React 共享的逻辑",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"tsup": "^8.0.0",
|
|
21
|
+
"@types/mdast": "^4.0.0",
|
|
22
|
+
"typescript": "^5.3.0"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@incremark/core": "0.2.3"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"incremark",
|
|
29
|
+
"shared",
|
|
30
|
+
"utils"
|
|
31
|
+
],
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/kingshuaishuai/incremark.git",
|
|
36
|
+
"directory": "packages/shared"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://www.incremark.com/",
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup",
|
|
41
|
+
"dev": "tsup --watch"
|
|
42
|
+
}
|
|
43
|
+
}
|