@incremark/shared 0.3.2 → 0.3.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/dist/index.d.ts CHANGED
@@ -78,6 +78,17 @@ declare function isServer(): boolean;
78
78
  */
79
79
  declare function isClipboardAvailable(): boolean;
80
80
 
81
+ /**
82
+ * @file id.ts - ID 生成工具
83
+ * @description 提供各种 ID 生成函数
84
+ */
85
+ /**
86
+ * 生成唯一的 parser ID
87
+ * @param prefix - ID 前缀,默认为 'parser'
88
+ * @returns 唯一的 parser ID
89
+ */
90
+ declare function generateParserId(prefix?: string): string;
91
+
81
92
  /**
82
93
  * Incremark 国际化类型定义
83
94
  */
@@ -112,4 +123,4 @@ declare const en: IncremarkLocale;
112
123
  */
113
124
  declare const zhCN: IncremarkLocale;
114
125
 
115
- export { type HtmlTagInfo, type HtmlWrapperNode, type IncremarkLocale, type TextNodeWithChunks, en, extractTagName, getStableText, hasChunks, isBrowser, isClipboardAvailable, isHtmlNode, isHtmlWrapperNode, isServer, processHtmlNodes, zhCN };
126
+ export { type HtmlTagInfo, type HtmlWrapperNode, type IncremarkLocale, type TextChunk, type TextNodeWithChunks, en, extractTagName, generateParserId, getStableText, hasChunks, isBrowser, isClipboardAvailable, isHtmlNode, isHtmlWrapperNode, isServer, processHtmlNodes, zhCN };
package/dist/index.js CHANGED
@@ -125,6 +125,11 @@ function isClipboardAvailable() {
125
125
  return typeof navigator !== "undefined" && !!navigator.clipboard;
126
126
  }
127
127
 
128
+ // src/id.ts
129
+ function generateParserId(prefix = "parser") {
130
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
131
+ }
132
+
128
133
  // src/locales/en.ts
129
134
  var en = {
130
135
  code: {
@@ -155,6 +160,6 @@ var zhCN = {
155
160
  };
156
161
  var zh_cn_default = zhCN;
157
162
 
158
- export { en_default as en, extractTagName, getStableText, hasChunks, isBrowser, isClipboardAvailable, isHtmlNode, isHtmlWrapperNode, isServer, processHtmlNodes, zh_cn_default as zhCN };
163
+ export { en_default as en, extractTagName, generateParserId, getStableText, hasChunks, isBrowser, isClipboardAvailable, isHtmlNode, isHtmlWrapperNode, isServer, processHtmlNodes, zh_cn_default as zhCN };
159
164
  //# sourceMappingURL=index.js.map
160
165
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/html.ts","../src/text.ts","../src/env.ts","../src/locales/en.ts","../src/locales/zh-cn.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","/**\n * 环境检测工具函数\n *\n * 用于 SSR 兼容性检测\n */\n\n/**\n * 检测是否在浏览器环境中\n */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined'\n}\n\n/**\n * 检测是否在服务器环境中 (SSR)\n */\nexport function isServer(): boolean {\n return typeof window === 'undefined'\n}\n\n/**\n * 检测 Clipboard API 是否可用\n */\nexport function isClipboardAvailable(): boolean {\n return typeof navigator !== 'undefined' && !!navigator.clipboard\n}\n","import type { IncremarkLocale } from './types'\n\n/**\n * 英文 locale\n */\nconst en: IncremarkLocale = {\n code: {\n copy: 'Copy code',\n copied: 'Code copied'\n },\n mermaid: {\n copy: 'Copy code',\n copied: 'Code copied',\n viewSource: 'View source code',\n preview: 'Preview diagram'\n }\n}\n\nexport default en\n","import type { IncremarkLocale } from './types'\n\n/**\n * 简体中文 locale\n */\nconst zhCN: IncremarkLocale = {\n code: {\n copy: '复制代码',\n copied: '代码已复制'\n },\n mermaid: {\n copy: '复制代码',\n copied: '代码已复制',\n viewSource: '查看源代码',\n preview: '预览图表'\n }\n}\n\nexport default zhCN\n"]}
1
+ {"version":3,"sources":["../src/html.ts","../src/text.ts","../src/env.ts","../src/id.ts","../src/locales/en.ts","../src/locales/zh-cn.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","/**\n * 环境检测工具函数\n *\n * 用于 SSR 兼容性检测\n */\n\n/**\n * 检测是否在浏览器环境中\n */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined'\n}\n\n/**\n * 检测是否在服务器环境中 (SSR)\n */\nexport function isServer(): boolean {\n return typeof window === 'undefined'\n}\n\n/**\n * 检测 Clipboard API 是否可用\n */\nexport function isClipboardAvailable(): boolean {\n return typeof navigator !== 'undefined' && !!navigator.clipboard\n}\n","/**\n * @file id.ts - ID 生成工具\n * @description 提供各种 ID 生成函数\n */\n\n/**\n * 生成唯一的 parser ID\n * @param prefix - ID 前缀,默认为 'parser'\n * @returns 唯一的 parser ID\n */\nexport function generateParserId(prefix = 'parser'): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n","import type { IncremarkLocale } from './types'\n\n/**\n * 英文 locale\n */\nconst en: IncremarkLocale = {\n code: {\n copy: 'Copy code',\n copied: 'Code copied'\n },\n mermaid: {\n copy: 'Copy code',\n copied: 'Code copied',\n viewSource: 'View source code',\n preview: 'Preview diagram'\n }\n}\n\nexport default en\n","import type { IncremarkLocale } from './types'\n\n/**\n * 简体中文 locale\n */\nconst zhCN: IncremarkLocale = {\n code: {\n copy: '复制代码',\n copied: '代码已复制'\n },\n mermaid: {\n copy: '复制代码',\n copied: '代码已复制',\n viewSource: '查看源代码',\n preview: '预览图表'\n }\n}\n\nexport default zhCN\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@incremark/shared",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Incremark shared utilities - Common logic and i18n support for Vue, React, and Svelte.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,7 @@
22
22
  "typescript": "^5.3.0"
23
23
  },
24
24
  "peerDependencies": {
25
- "@incremark/core": "0.3.2"
25
+ "@incremark/core": "0.3.4"
26
26
  },
27
27
  "keywords": [
28
28
  "incremark",