parse_html_to_node 0.0.1 → 0.0.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # parse_html
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/parse_html)](https://www.npmjs.com/package/parse_html)
3
+ [![npm version](https://img.shields.io/npm/v/parse_html_to_node)](https://www.npmjs.com/package/parse_html_to_node)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
6
6
 
@@ -19,25 +19,25 @@
19
19
  ## 安装
20
20
 
21
21
  ```bash
22
- npm install parse_html
22
+ npm install parse_html_to_node
23
23
  ```
24
24
 
25
25
 
26
26
 
27
27
  ```bash
28
- yarn add parse_html
28
+ yarn add parse_html_to_node
29
29
  ```
30
30
 
31
31
 
32
32
 
33
33
  ```bash
34
- pnpm add parse_html
34
+ pnpm add parse_html_to_node
35
35
  ```
36
36
 
37
37
  ## 快速开始
38
38
 
39
39
  ```javascript
40
- import Node from 'parse_html';
40
+ import Node from 'parse_html_to_node';
41
41
 
42
42
  // 解析单根HTML
43
43
  const html = `
@@ -183,7 +183,7 @@ const htmlString = node.getHtml();
183
183
  ### 1. 多根节点解析
184
184
 
185
185
  ```javascript
186
- import Node from 'parse_html';
186
+ import Node from 'parse_html_to_node';
187
187
 
188
188
  const multiRootHtml = `
189
189
  <p>第一段文本</p>
@@ -200,7 +200,7 @@ console.log(fragmentNode.getHtml()); // 输出拼接后的完整HTML
200
200
  ### 2. DOM操作
201
201
 
202
202
  ```javascript
203
- import Node from 'parse_html';
203
+ import Node from 'parse_html_to_node';
204
204
 
205
205
  const parent = new Node('<div id="parent"></div>');
206
206
 
@@ -219,7 +219,7 @@ console.log(parent.getHtml());
219
219
  ### 3. 属性和样式操作
220
220
 
221
221
  ```javascript
222
- import Node from 'parse_html';
222
+ import Node from 'parse_html_to_node';
223
223
 
224
224
  const node = new Node('<div>Test</div>');
225
225
 
@@ -247,7 +247,7 @@ console.log(node.getStyle('color')); // "green"
247
247
  ### 4. 文本节点处理
248
248
 
249
249
  ```javascript
250
- import Node from 'parse_html';
250
+ import Node from 'parse_html_to_node';
251
251
 
252
252
  const html = 'Hello <strong>World</strong>!';
253
253
  const node = new Node(html);
@@ -283,7 +283,7 @@ pnpm run build
283
283
  ### 项目结构
284
284
 
285
285
  ```
286
- parse_html/
286
+ parse_html_to_node/
287
287
  ├── lib/
288
288
  │ └── index.ts # 源代码
289
289
  ├── dist/ # 构建输出
@@ -309,5 +309,5 @@ parse_html/
309
309
  ## 相关链接
310
310
 
311
311
  - [GitHub仓库](https://github.com/Easy-Martin/parse_html)
312
- - [npm包](https://www.npmjs.com/package/parse_html)
312
+ - [npm包](https://www.npmjs.com/package/parse_html_to_node)
313
313
  - [问题反馈](https://github.com/Easy-Martin/parse_html/issues)
@@ -0,0 +1,2 @@
1
+ "use strict";const C=["img","br","input","meta","link","hr","area","base","col","embed","param","source","track","wbr"];function M(o){return o.replace(/[A-Z]/g,t=>`-${t.toLowerCase()}`)}function S(o){return o.replace(/-([a-z])/g,(t,e)=>e.toUpperCase())}function A(o){const t={};if(!o)return t;const e=/([a-zA-Z0-9-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/g;let n;for(;(n=e.exec(o))!==null;){const[,s,r,l,a]=n,h=r||l||a||"";t[s]=h}if(t.style){const s={},r=/([a-zA-Z0-9-]+)\s*:\s*([^;]+)/g;let l;for(;(l=r.exec(t.style))!==null;){const[,a,h]=l;s[S(a.trim())]=h.trim()}t.styleObj=s,delete t.style}return t}function g(o){if(o=o.trim(),!o)return null;if(!o.startsWith("<"))return{tagName:"#text",textContent:o,attributes:{},styles:{},children:[],parent:null};const t=/<([a-zA-Z0-9]+)\s*(.*?)\/?>/,e=o.match(t);if(e){const[,c,i]=e;if(C.includes(c.toLowerCase())){const N=A(i);return{tagName:c.toLowerCase(),attributes:N,styles:N.styleObj||{},textContent:"",children:[],parent:null}}}const n=/<([a-zA-Z0-9]+)\s*(.*?)>/,s=o.match(n);if(!s)return null;const[r,l,a]=s,h=l.toLowerCase(),u=`</${h}>`;let x=-1,d=1,m=r.length;for(;m<o.length&&d>0;){const c=o.indexOf(`<${h}`,m),i=o.indexOf(u,m);if(i===-1)break;c!==-1&&c<i?(d++,m=c+`<${h}`.length):(d--,d===0&&(x=i),m=i+u.length)}const L=x!==-1?o.slice(r.length,x).trim():o.slice(r.length).trim(),I=A(a),w={tagName:h,attributes:I,styles:I.styleObj||{},textContent:"",children:[],parent:null};if(L){const c=[];let i=L;for(;i;){const N=i.indexOf("<");if(N===-1){const p=g(i);p&&c.push(p),i=""}else if(N>0){const p=g(i.slice(0,N));p&&c.push(p),i=i.slice(N)}else{const p=i.match(n);if(!p){const f=g(i);f&&c.push(f),i="";continue}const E=p[1].toLowerCase(),$=`</${E}>`;if(C.includes(E)){const f=g(i.slice(0,p[0].length));f&&c.push(f),i=i.slice(p[0].length).trim();continue}let O=-1,j=1,T=p[0].length;for(;T<i.length&&j>0;){const f=i.indexOf(`<${E}`,T),y=i.indexOf($,T);if(y===-1)break;f!==-1&&f<y?(j++,T=f+`<${E}`.length):(j--,j===0&&(O=y),T=y+$.length)}if(O!==-1){const f=i.slice(0,O+$.length),y=g(f);y&&c.push(y),i=i.slice(O+$.length).trim()}else{const f=g(i);f&&c.push(f),i=""}}}w.children=c.filter(Boolean),w.children.length===1&&w.children[0].tagName==="#text"&&(w.textContent=w.children[0].textContent,w.children=[])}return w}function k(o){if(o=o.trim(),!o)return[];const t=[];let e=o;for(;e;){const n=e.indexOf("<");if(n===-1){const s=g(e);s&&t.push(s),e=""}else if(n>0){const s=g(e.slice(0,n));s&&t.push(s),e=e.slice(n)}else{const s=/<([a-zA-Z0-9]+)\s*(.*?)>/,r=e.match(s);if(!r){const a=g(e);a&&t.push(a),e="";continue}const l=r[1].toLowerCase();if(C.includes(l)){const a=/<([a-zA-Z0-9]+)\s*(.*?)\/?>/,h=e.match(a);if(h){const u=g(h[0]);u&&t.push(u),e=e.slice(h[0].length).trim()}else{const u=g(e);u&&t.push(u),e=""}}else{const a=`</${l}>`;let h=-1,u=1,x=r[0].length;for(;x<e.length&&u>0;){const d=e.indexOf(`<${l}`,x),m=e.indexOf(a,x);if(m===-1)break;d!==-1&&d<m?(u++,x=d+`<${l}`.length):(u--,u===0&&(h=m),x=m+a.length)}if(h!==-1){const d=e.slice(0,h+a.length),m=g(d);m&&t.push(m),e=e.slice(h+a.length).trim()}else{const d=g(e);d&&t.push(d),e=""}}}}return t.filter(Boolean)}class b{constructor(t){if(typeof t!="string")throw new Error("初始化Node必须传入HTML字符串");const e=t.trim();if(!e)throw new Error("无法解析空的HTML字符串");const n=g(e),s=k(e);if(this.tagName="",this.attributes={},this.styles={},this.textContent="",this.children=[],this.parent=null,n&&s.length===1)this.tagName=n.tagName,this.attributes={...n.attributes},this.styles={...n.styles},this.textContent=n.textContent||"",n.children.length>0&&(this.children=n.children.map(r=>{const l=new b(this.#t(r));return l.parent=this,l}));else if(s.length>0)this.tagName="#fragment",this.attributes={},this.styles={},this.textContent="",this.children=s.map(r=>{const l=new b(this.#t(r));return l.parent=this,l});else throw new Error("无法解析无效的HTML字符串")}#t(t){if(t.tagName==="#text")return t.textContent;let e=`<${t.tagName}`;const n={...t.attributes};if(Object.keys(t.styles).length>0){const r=Object.entries(t.styles).map(([l,a])=>`${M(l)}: ${a}`).join("; ");n.style=r}for(const[r,l]of Object.entries(n))r!=="styleObj"&&typeof l=="string"&&(e+=` ${r}="${l}"`);if(C.includes(t.tagName))return e+="/>",e;e+=">";let s=t.textContent;return t.children.length>0&&(s+=t.children.map(r=>this.#t(r)).join("")),`${e}${s}</${t.tagName}>`}child(){return[...this.children]}before(t){if(!this.parent)throw new Error("当前节点没有父节点,无法执行before操作");const e=this.#e(t),n=this.parent.children.findIndex(s=>s===this);if(n===-1)throw new Error("当前节点不在父节点的子节点列表中");return this.parent.children.splice(n,0,e),e.parent=this.parent,this}after(t){if(!this.parent)throw new Error("当前节点没有父节点,无法执行after操作");const e=this.#e(t),n=this.parent.children.findIndex(s=>s===this);if(n===-1)throw new Error("当前节点不在父节点的子节点列表中");return this.parent.children.splice(n+1,0,e),e.parent=this.parent,this}insert(t,e){if(typeof t!="number"||t<0||t>this.children.length)throw new Error(`插入位置${t}无效,必须是0到${this.children.length}之间的整数`);const n=this.#e(e);return n.tagName==="#fragment"?n.children.forEach((s,r)=>{s.parent=this,this.children.splice(t+r,0,s)}):(this.children.splice(t,0,n),n.parent=this),this}getAttr(t){if(this.tagName==="#fragment"||this.tagName==="#text")return null;if(typeof t!="string")throw new Error("属性名必须是字符串");return typeof this.attributes[t]=="string"?this.attributes[t]:null}setAttr(t,e){if(this.tagName==="#fragment"||this.tagName==="#text")throw new Error("片段/文本节点不支持设置属性");if(typeof t!="string")throw new Error("属性名必须是字符串");return e==null?delete this.attributes[t]:this.attributes[t]=String(e),this}setAttrs(t){if(this.tagName==="#fragment"||this.tagName==="#text")throw new Error("片段/文本节点不支持设置属性");if(typeof t!="object"||t===null)throw new Error("属性对象必须是非空对象");for(const[e,n]of Object.entries(t))this.setAttr(e,n);return this}getStyle(t){if(this.tagName==="#fragment"||this.tagName==="#text")return null;if(typeof t!="string")throw new Error("样式属性名必须是字符串");const e=S(t);return this.styles[e]||this.styles[t]||null}setStyle(t,e){if(this.tagName==="#fragment"||this.tagName==="#text")throw new Error("片段/文本节点不支持设置样式");if(typeof t!="string")throw new Error("样式属性名必须是字符串");const n=S(t);return e==null?(delete this.styles[n],delete this.styles[t]):this.styles[n]=String(e),this}setStyles(t){if(this.tagName==="#fragment"||this.tagName==="#text")throw new Error("片段/文本节点不支持设置样式");if(typeof t!="object"||t===null)throw new Error("样式对象必须是非空对象");for(const[e,n]of Object.entries(t))this.setStyle(e,n);return this}getHtml(){if(this.tagName==="#text")return this.textContent;if(this.tagName==="#fragment")return this.children.map(s=>s.getHtml()).join("");let t=`<${this.tagName}`;const e={...this.attributes};if(Object.keys(this.styles).length>0){const s=Object.entries(this.styles).map(([r,l])=>`${M(r)}: ${l}`).join("; ");e.style=s}for(const[s,r]of Object.entries(e))if(s!=="styleObj"&&typeof r=="string"){const l=r.replace(/"/g,"&quot;");t+=` ${s}="${l}"`}if(C.includes(this.tagName))return t+="/>",t;t+=">";let n=this.textContent;return this.children.length>0&&(n+=this.children.map(s=>s.getHtml()).join("")),`${t}${n}</${this.tagName}>`}#e(t){if(t instanceof b)return t;if(typeof t=="string")return new b(t);throw new Error("插入的节点必须是HTML字符串或Node实例")}}module.exports=b;
2
+ //# sourceMappingURL=parse_html.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse_html.cjs.js","sources":["../lib/parse_html.ts"],"sourcesContent":["// 定义核心接口:扩展属性对象(包含内部使用的styleObj)\ninterface IAttributeData {\n [key: string]: string | Record<string, string> | undefined; // 允许值是字符串、样式对象或undefined\n styleObj?: Record<string, string>; // 新增:显式声明styleObj属性(可选)\n}\n\n// 定义核心接口:节点数据结构(解析后的原始数据)\ninterface INodeData {\n tagName: string; // #text | #fragment | 标签名(如div/p)\n textContent: string;\n attributes: IAttributeData; // 修改:使用扩展后的属性接口\n styles: Record<string, string>; // 样式键值对(驼峰格式)\n children: INodeData[];\n parent: INodeData | null;\n}\n\n// 自闭合标签常量(只读数组)\nconst SELF_CLOSING_TAGS: readonly string[] = [\"img\", \"br\", \"input\", \"meta\", \"link\", \"hr\", \"area\", \"base\", \"col\", \"embed\", \"param\", \"source\", \"track\", \"wbr\"];\n\n// 工具函数:驼峰转短横线(用于样式属性)\nfunction camelToKebab(str: string): string {\n return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);\n}\n\n// 工具函数:短横线转驼峰(用于样式属性)\nfunction kebabToCamel(str: string): string {\n return str.replace(/-([a-z])/g, (_: string, match: string) => match.toUpperCase());\n}\n\n// 工具函数:解析属性字符串为属性对象(修改返回类型为IAttributeData)\nfunction parseAttributes(attrStr: string): IAttributeData {\n const attrs: IAttributeData = {}; // 修改:使用扩展后的属性接口\n if (!attrStr) return attrs;\n\n // 匹配属性键值对:key=\"value\" / key='value' / key=value\n const attrRegex = /([a-zA-Z0-9-]+)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s>]+))/g;\n let match: RegExpExecArray | null;\n\n while ((match = attrRegex.exec(attrStr)) !== null) {\n const [, key, doubleVal, singleVal, noQuoteVal] = match;\n const value = doubleVal || singleVal || noQuoteVal || \"\";\n attrs[key] = value;\n }\n\n // 解析style属性为样式对象(内部属性,不对外输出)\n if (attrs.style) {\n const styleObj: Record<string, string> = {};\n const styleRegex = /([a-zA-Z0-9-]+)\\s*:\\s*([^;]+)/g;\n let styleMatch: RegExpExecArray | null;\n\n while ((styleMatch = styleRegex.exec(attrs.style as string)) !== null) {\n const [, prop, val] = styleMatch;\n styleObj[kebabToCamel(prop.trim())] = val.trim();\n }\n\n attrs.styleObj = styleObj; // 现在类型匹配,无TS错误\n delete attrs.style; // 替换为结构化的样式对象\n }\n\n return attrs;\n}\n\n// 核心工具函数:解析单个节点(内部使用)\nfunction parseSingleNode(html: string): INodeData | null {\n html = html.trim();\n if (!html) return null;\n\n // 文本节点(非标签内容)\n if (!html.startsWith(\"<\")) {\n return {\n tagName: \"#text\",\n textContent: html,\n attributes: {}, // 符合IAttributeData类型\n styles: {},\n children: [],\n parent: null,\n };\n }\n\n const selfClosingRegex = /<([a-zA-Z0-9]+)\\s*(.*?)\\/?>/;\n const selfClosingMatch = html.match(selfClosingRegex);\n\n // 处理自闭合标签\n if (selfClosingMatch) {\n const [, tagName, attrStr] = selfClosingMatch;\n if (SELF_CLOSING_TAGS.includes(tagName.toLowerCase())) {\n const attrs = parseAttributes(attrStr);\n return {\n tagName: tagName.toLowerCase(),\n attributes: attrs, // 类型匹配\n styles: attrs.styleObj || {}, // styleObj是Record<string, string>,符合styles类型\n textContent: \"\",\n children: [],\n parent: null,\n };\n }\n }\n\n // 匹配开始标签\n const startTagRegex = /<([a-zA-Z0-9]+)\\s*(.*?)>/;\n const startTagMatch = html.match(startTagRegex);\n if (!startTagMatch) return null;\n\n const [startTag, tagName, attrStr] = startTagMatch;\n const lowerTagName = tagName.toLowerCase();\n const endTag = `</${lowerTagName}>`;\n\n // 查找匹配的结束标签(处理嵌套)\n let endTagIndex = -1;\n let tagCount = 1;\n let currentIndex = startTag.length;\n\n while (currentIndex < html.length && tagCount > 0) {\n const nextStart = html.indexOf(`<${lowerTagName}`, currentIndex);\n const nextEnd = html.indexOf(endTag, currentIndex);\n\n if (nextEnd === -1) break;\n if (nextStart !== -1 && nextStart < nextEnd) {\n tagCount++;\n currentIndex = nextStart + `<${lowerTagName}`.length;\n } else {\n tagCount--;\n if (tagCount === 0) endTagIndex = nextEnd;\n currentIndex = nextEnd + endTag.length;\n }\n }\n\n // 提取标签内容(开始标签和结束标签之间)\n const content = endTagIndex !== -1 ? html.slice(startTag.length, endTagIndex).trim() : html.slice(startTag.length).trim();\n\n // 构建基础节点数据\n const attrs = parseAttributes(attrStr);\n const nodeData: INodeData = {\n tagName: lowerTagName,\n attributes: attrs, // 类型匹配\n styles: attrs.styleObj || {}, // 类型匹配\n textContent: \"\",\n children: [],\n parent: null,\n };\n\n // 解析子节点\n if (content) {\n const childNodes: INodeData[] = [];\n let remaining = content;\n\n while (remaining) {\n const tagStart = remaining.indexOf(\"<\");\n if (tagStart === -1) {\n // 纯文本内容\n const textNode = parseSingleNode(remaining);\n if (textNode) childNodes.push(textNode);\n remaining = \"\";\n } else {\n if (tagStart > 0) {\n // 标签前的文本\n const textNode = parseSingleNode(remaining.slice(0, tagStart));\n if (textNode) childNodes.push(textNode);\n remaining = remaining.slice(tagStart);\n } else {\n // 解析嵌套标签\n const tempTagMatch = remaining.match(startTagRegex);\n if (!tempTagMatch) {\n const textNode = parseSingleNode(remaining);\n if (textNode) childNodes.push(textNode);\n remaining = \"\";\n continue;\n }\n\n const tempTagName = tempTagMatch[1].toLowerCase();\n const tempEndTag = `</${tempTagName}>`;\n\n // 自闭合标签直接处理\n if (SELF_CLOSING_TAGS.includes(tempTagName)) {\n const selfNode = parseSingleNode(remaining.slice(0, tempTagMatch[0].length));\n if (selfNode) childNodes.push(selfNode);\n remaining = remaining.slice(tempTagMatch[0].length).trim();\n continue;\n }\n\n // 查找嵌套标签的结束位置\n let tempEndIndex = -1;\n let tempTagCount = 1;\n let tempCurrentIndex = tempTagMatch[0].length;\n\n while (tempCurrentIndex < remaining.length && tempTagCount > 0) {\n const nextTempStart = remaining.indexOf(`<${tempTagName}`, tempCurrentIndex);\n const nextTempEnd = remaining.indexOf(tempEndTag, tempCurrentIndex);\n\n if (nextTempEnd === -1) break;\n if (nextTempStart !== -1 && nextTempStart < nextTempEnd) {\n tempTagCount++;\n tempCurrentIndex = nextTempStart + `<${tempTagName}`.length;\n } else {\n tempTagCount--;\n if (tempTagCount === 0) tempEndIndex = nextTempEnd;\n tempCurrentIndex = nextTempEnd + tempEndTag.length;\n }\n }\n\n // 提取子节点HTML并递归解析\n if (tempEndIndex !== -1) {\n const childHTML = remaining.slice(0, tempEndIndex + tempEndTag.length);\n const childNode = parseSingleNode(childHTML);\n if (childNode) childNodes.push(childNode);\n remaining = remaining.slice(tempEndIndex + tempEndTag.length).trim();\n } else {\n const childNode = parseSingleNode(remaining);\n if (childNode) childNodes.push(childNode);\n remaining = \"\";\n }\n }\n }\n }\n\n // 过滤无效节点并赋值\n nodeData.children = childNodes.filter(Boolean);\n // 单一文本子节点直接合并到textContent\n if (nodeData.children.length === 1 && nodeData.children[0].tagName === \"#text\") {\n nodeData.textContent = nodeData.children[0].textContent;\n nodeData.children = [];\n }\n }\n\n return nodeData;\n}\n\n// 新增:解析HTML片段(支持多根节点)\nfunction parseHTMLFragment(html: string): INodeData[] {\n html = html.trim();\n if (!html) return [];\n\n const fragmentNodes: INodeData[] = [];\n let remaining = html;\n\n while (remaining) {\n const tagStart = remaining.indexOf(\"<\");\n if (tagStart === -1) {\n // 剩余纯文本\n const textNode = parseSingleNode(remaining);\n if (textNode) fragmentNodes.push(textNode);\n remaining = \"\";\n } else {\n if (tagStart > 0) {\n // 标签前的文本节点\n const textNode = parseSingleNode(remaining.slice(0, tagStart));\n if (textNode) fragmentNodes.push(textNode);\n remaining = remaining.slice(tagStart);\n } else {\n // 解析单个标签节点\n const startTagRegex = /<([a-zA-Z0-9]+)\\s*(.*?)>/;\n const tempTagMatch = remaining.match(startTagRegex);\n\n if (!tempTagMatch) {\n const textNode = parseSingleNode(remaining);\n if (textNode) fragmentNodes.push(textNode);\n remaining = \"\";\n continue;\n }\n\n const tempTagName = tempTagMatch[1].toLowerCase();\n\n // 自闭合标签\n if (SELF_CLOSING_TAGS.includes(tempTagName)) {\n const selfClosingRegex = /<([a-zA-Z0-9]+)\\s*(.*?)\\/?>/;\n const selfClosingMatch = remaining.match(selfClosingRegex);\n\n if (selfClosingMatch) {\n const selfNode = parseSingleNode(selfClosingMatch[0]);\n if (selfNode) fragmentNodes.push(selfNode);\n remaining = remaining.slice(selfClosingMatch[0].length).trim();\n } else {\n const textNode = parseSingleNode(remaining);\n if (textNode) fragmentNodes.push(textNode);\n remaining = \"\";\n }\n } else {\n // 非自闭合标签,找匹配的结束标签\n const tempEndTag = `</${tempTagName}>`;\n let tempEndIndex = -1;\n let tempTagCount = 1;\n let tempCurrentIndex = tempTagMatch[0].length;\n\n while (tempCurrentIndex < remaining.length && tempTagCount > 0) {\n const nextTempStart = remaining.indexOf(`<${tempTagName}`, tempCurrentIndex);\n const nextTempEnd = remaining.indexOf(tempEndTag, tempCurrentIndex);\n\n if (nextTempEnd === -1) break;\n if (nextTempStart !== -1 && nextTempStart < nextTempEnd) {\n tempTagCount++;\n tempCurrentIndex = nextTempStart + `<${tempTagName}`.length;\n } else {\n tempTagCount--;\n if (tempTagCount === 0) tempEndIndex = nextTempEnd;\n tempCurrentIndex = nextTempEnd + tempEndTag.length;\n }\n }\n\n if (tempEndIndex !== -1) {\n const childHTML = remaining.slice(0, tempEndIndex + tempEndTag.length);\n const childNode = parseSingleNode(childHTML);\n if (childNode) fragmentNodes.push(childNode);\n remaining = remaining.slice(tempEndIndex + tempEndTag.length).trim();\n } else {\n const childNode = parseSingleNode(remaining);\n if (childNode) fragmentNodes.push(childNode);\n remaining = \"\";\n }\n }\n }\n }\n }\n\n // 过滤无效节点\n return fragmentNodes.filter(Boolean);\n}\n\n// 核心Node类实现(支持片段/多根节点)\nclass Node {\n public tagName: string;\n public attributes: IAttributeData; // 修改:使用扩展后的属性接口\n public styles: Record<string, string>;\n public textContent: string;\n public children: Node[];\n public parent: Node | null;\n\n /**\n * 构造函数:通过HTML字符串初始化节点(支持单根/多根)\n * @param {string} html - HTML字符串(单根/多根均可)\n */\n constructor(html: string) {\n if (typeof html !== \"string\") {\n throw new Error(\"初始化Node必须传入HTML字符串\");\n }\n\n const htmlTrimmed = html.trim();\n if (!htmlTrimmed) {\n throw new Error(\"无法解析空的HTML字符串\");\n }\n\n // 尝试解析为单一节点\n const singleNodeData = parseSingleNode(htmlTrimmed);\n // 解析为片段(多根节点)\n const fragmentNodeData = parseHTMLFragment(htmlTrimmed);\n\n // 初始化默认属性\n this.tagName = \"\";\n this.attributes = {}; // 符合IAttributeData类型\n this.styles = {};\n this.textContent = \"\";\n this.children = [];\n this.parent = null;\n\n // 节点核心属性赋值\n if (singleNodeData && fragmentNodeData.length === 1) {\n // 单根节点\n this.tagName = singleNodeData.tagName;\n this.attributes = { ...singleNodeData.attributes };\n this.styles = { ...singleNodeData.styles };\n this.textContent = singleNodeData.textContent || \"\";\n\n // 子节点转换为Node实例\n if (singleNodeData.children.length > 0) {\n this.children = singleNodeData.children.map((childData) => {\n const childNode = new Node(this.#generateHTMLFromData(childData));\n childNode.parent = this;\n return childNode;\n });\n }\n } else if (fragmentNodeData.length > 0) {\n // 多根节点 → 标记为片段节点\n this.tagName = \"#fragment\";\n this.attributes = {}; // 符合IAttributeData类型\n this.styles = {};\n this.textContent = \"\";\n\n // 片段的子节点是多个根节点\n this.children = fragmentNodeData.map((childData) => {\n const childNode = new Node(this.#generateHTMLFromData(childData));\n childNode.parent = this;\n return childNode;\n });\n } else {\n throw new Error(\"无法解析无效的HTML字符串\");\n }\n }\n\n /**\n * 私有方法:从节点数据生成HTML字符串(用于子节点初始化)\n * @param {INodeData} nodeData - 节点数据\n * @returns {string} HTML字符串\n */\n #generateHTMLFromData(nodeData: INodeData): string {\n if (nodeData.tagName === \"#text\") return nodeData.textContent;\n\n // 构建开始标签\n let startTag = `<${nodeData.tagName}`;\n const attrs = { ...nodeData.attributes };\n\n // 拼接样式属性(覆盖原始style)\n if (Object.keys(nodeData.styles).length > 0) {\n const styleStr = Object.entries(nodeData.styles)\n .map(([key, val]) => `${camelToKebab(key)}: ${val}`)\n .join(\"; \");\n attrs.style = styleStr;\n }\n\n // 拼接所有属性:过滤内部属性styleObj\n for (const [key, val] of Object.entries(attrs)) {\n if (key === \"styleObj\") continue; // 核心修复:跳过内部样式对象属性\n // 确保val是字符串(IAttributeData中除了styleObj都是string)\n if (typeof val === \"string\") {\n startTag += ` ${key}=\"${val}\"`;\n }\n }\n\n // 自闭合标签处理\n if (SELF_CLOSING_TAGS.includes(nodeData.tagName)) {\n startTag += \"/>\";\n return startTag;\n }\n\n startTag += \">\";\n\n // 拼接内容(文本+子节点)\n let content = nodeData.textContent;\n if (nodeData.children.length > 0) {\n content += nodeData.children.map((child) => this.#generateHTMLFromData(child)).join(\"\");\n }\n\n return `${startTag}${content}</${nodeData.tagName}>`;\n }\n\n /**\n * 子集管理:获取当前节点的所有子节点\n * @returns {Node[]} 子节点数组(浅拷贝)\n */\n child(): Node[] {\n return [...this.children];\n }\n\n /**\n * DOM操作:在当前节点之后插入新元素\n * @param {string|Node} newNode - 要插入的HTML字符串或Node实例\n * @returns {Node} 当前节点(链式调用)\n */\n before(newNode: string | Node): Node {\n if (!this.parent) {\n throw new Error(\"当前节点没有父节点,无法执行before操作\");\n }\n\n const nodeToInsert = this.#convertToNode(newNode);\n const currentIndex = this.parent.children.findIndex((child) => child === this);\n\n if (currentIndex === -1) {\n throw new Error(\"当前节点不在父节点的子节点列表中\");\n }\n\n // 插入到当前节点前一个位置\n this.parent.children.splice(currentIndex, 0, nodeToInsert);\n nodeToInsert.parent = this.parent;\n\n return this;\n }\n\n /**\n * DOM操作:在当前节点之后插入新元素\n * @param {string|Node} newNode - 要插入的HTML字符串或Node实例\n * @returns {Node} 当前节点(链式调用)\n */\n after(newNode: string | Node): Node {\n if (!this.parent) {\n throw new Error(\"当前节点没有父节点,无法执行after操作\");\n }\n\n const nodeToInsert = this.#convertToNode(newNode);\n const currentIndex = this.parent.children.findIndex((child) => child === this);\n\n if (currentIndex === -1) {\n throw new Error(\"当前节点不在父节点的子节点列表中\");\n }\n\n // 插入到当前节点下一个位置\n this.parent.children.splice(currentIndex + 1, 0, nodeToInsert);\n nodeToInsert.parent = this.parent;\n\n return this;\n }\n\n /**\n * DOM操作:在指定位置插入新元素\n * @param {number} position - 插入位置(0 ~ children.length)\n * @param {string|Node} newNode - 要插入的HTML字符串或Node实例\n * @returns {Node} 当前节点(链式调用)\n */\n insert(position: number, newNode: string | Node): Node {\n if (typeof position !== \"number\" || position < 0 || position > this.children.length) {\n throw new Error(`插入位置${position}无效,必须是0到${this.children.length}之间的整数`);\n }\n\n const nodeToInsert = this.#convertToNode(newNode);\n // 如果插入的是片段节点,展开其所有子节点(符合DOM标准)\n if (nodeToInsert.tagName === \"#fragment\") {\n nodeToInsert.children.forEach((child, index) => {\n child.parent = this;\n this.children.splice(position + index, 0, child);\n });\n } else {\n this.children.splice(position, 0, nodeToInsert);\n nodeToInsert.parent = this;\n }\n\n return this;\n }\n\n /**\n * 属性操作:获取指定属性的值\n * @param {string} attrName - 属性名\n * @returns {string|null} 属性值(不存在返回null)\n */\n getAttr(attrName: string): string | null {\n if (this.tagName === \"#fragment\" || this.tagName === \"#text\") {\n return null; // 片段/文本节点无属性\n }\n if (typeof attrName !== \"string\") {\n throw new Error(\"属性名必须是字符串\");\n }\n // 只返回字符串类型的属性(排除styleObj)\n return typeof this.attributes[attrName] === \"string\" ? this.attributes[attrName] : null;\n }\n\n /**\n * 属性操作:设置指定属性的值\n * @param {string} attrName - 属性名\n * @param {string|null|undefined} value - 属性值(null/undefined删除属性)\n * @returns {Node} 当前节点(链式调用)\n */\n setAttr(attrName: string, value: string | null | undefined): Node {\n if (this.tagName === \"#fragment\" || this.tagName === \"#text\") {\n throw new Error(\"片段/文本节点不支持设置属性\");\n }\n if (typeof attrName !== \"string\") {\n throw new Error(\"属性名必须是字符串\");\n }\n\n if (value === null || value === undefined) {\n delete this.attributes[attrName];\n } else {\n // 确保属性值是字符串(符合IAttributeData的基础约束)\n this.attributes[attrName] = String(value);\n }\n\n return this;\n }\n\n /**\n * 属性操作:批量设置多个属性\n * @param {Record<string, string | null | undefined>} attrs - 包含属性名-属性值键值对的对象\n * @returns {Node} 当前节点(链式调用)\n */\n setAttrs(attrs: Record<string, string | null | undefined>): Node {\n if (this.tagName === \"#fragment\" || this.tagName === \"#text\") {\n throw new Error(\"片段/文本节点不支持设置属性\");\n }\n if (typeof attrs !== \"object\" || attrs === null) {\n throw new Error(\"属性对象必须是非空对象\");\n }\n\n for (const [attrName, value] of Object.entries(attrs)) {\n this.setAttr(attrName, value);\n }\n\n return this;\n }\n\n /**\n * 样式操作:获取指定样式属性的值\n * @param {string} styleProp - 样式属性名(支持驼峰/短横线)\n * @returns {string|null} 样式值(不存在返回null)\n */\n getStyle(styleProp: string): string | null {\n if (this.tagName === \"#fragment\" || this.tagName === \"#text\") {\n return null; // 片段/文本节点无样式\n }\n if (typeof styleProp !== \"string\") {\n throw new Error(\"样式属性名必须是字符串\");\n }\n\n const camelProp = kebabToCamel(styleProp);\n return this.styles[camelProp] || this.styles[styleProp] || null;\n }\n\n /**\n * 样式操作:设置指定样式属性的值\n * @param {string} styleProp - 样式属性名(支持驼峰/短横线)\n * @param {string|null|undefined} value - 样式值(null/undefined删除样式)\n * @returns {Node} 当前节点(链式调用)\n */\n setStyle(styleProp: string, value: string | null | undefined): Node {\n if (this.tagName === \"#fragment\" || this.tagName === \"#text\") {\n throw new Error(\"片段/文本节点不支持设置样式\");\n }\n if (typeof styleProp !== \"string\") {\n throw new Error(\"样式属性名必须是字符串\");\n }\n\n const camelProp = kebabToCamel(styleProp);\n if (value === null || value === undefined) {\n delete this.styles[camelProp];\n delete this.styles[styleProp];\n } else {\n this.styles[camelProp] = String(value);\n }\n\n return this;\n }\n\n /**\n * 样式操作:批量设置多个样式属性\n * @param {Record<string, string | null | undefined>} styles - 包含样式属性名-样式值键值对的对象\n * @returns {Node} 当前节点(链式调用)\n */\n setStyles(styles: Record<string, string | null | undefined>): Node {\n if (this.tagName === \"#fragment\" || this.tagName === \"#text\") {\n throw new Error(\"片段/文本节点不支持设置样式\");\n }\n if (typeof styles !== \"object\" || styles === null) {\n throw new Error(\"样式对象必须是非空对象\");\n }\n\n for (const [styleProp, value] of Object.entries(styles)) {\n this.setStyle(styleProp, value);\n }\n\n return this;\n }\n\n /**\n * 获取当前节点的完整HTML文本\n * @returns {string} HTML字符串\n */\n getHtml(): string {\n // 文本节点直接返回文本\n if (this.tagName === \"#text\") {\n return this.textContent;\n }\n\n // 片段节点返回所有子节点的HTML拼接\n if (this.tagName === \"#fragment\") {\n return this.children.map((child) => child.getHtml()).join(\"\");\n }\n\n // 普通元素节点\n let startTag = `<${this.tagName}`;\n const attrs = { ...this.attributes };\n\n // 拼接样式属性(覆盖原始style)\n if (Object.keys(this.styles).length > 0) {\n const styleStr = Object.entries(this.styles)\n .map(([key, val]) => `${camelToKebab(key)}: ${val}`)\n .join(\"; \");\n attrs.style = styleStr;\n }\n\n // 拼接所有属性:过滤内部属性styleObj,确保值是字符串\n for (const [key, val] of Object.entries(attrs)) {\n if (key === \"styleObj\") continue; // 核心修复:跳过内部样式对象属性\n if (typeof val === \"string\") {\n const safeVal = val.replace(/\"/g, \"&quot;\");\n startTag += ` ${key}=\"${safeVal}\"`;\n }\n }\n\n // 自闭合标签处理\n if (SELF_CLOSING_TAGS.includes(this.tagName)) {\n startTag += \"/>\";\n return startTag;\n }\n\n startTag += \">\";\n\n // 拼接内容(文本+子节点HTML)\n let content = this.textContent;\n if (this.children.length > 0) {\n content += this.children.map((child) => child.getHtml()).join(\"\");\n }\n\n return `${startTag}${content}</${this.tagName}>`;\n }\n\n /**\n * 私有辅助方法:统一转换插入的节点为Node实例\n * @param {string|Node} node - HTML字符串或Node实例\n * @returns {Node} Node实例\n */\n #convertToNode(node: string | Node): Node {\n if (node instanceof Node) return node;\n if (typeof node === \"string\") return new Node(node);\n throw new Error(\"插入的节点必须是HTML字符串或Node实例\");\n }\n}\n\nexport default Node;\n"],"names":["SELF_CLOSING_TAGS","camelToKebab","str","match","kebabToCamel","_","parseAttributes","attrStr","attrs","attrRegex","key","doubleVal","singleVal","noQuoteVal","value","styleObj","styleRegex","styleMatch","prop","val","parseSingleNode","html","selfClosingRegex","selfClosingMatch","tagName","startTagRegex","startTagMatch","startTag","lowerTagName","endTag","endTagIndex","tagCount","currentIndex","nextStart","nextEnd","content","nodeData","childNodes","remaining","tagStart","textNode","tempTagMatch","tempTagName","tempEndTag","selfNode","tempEndIndex","tempTagCount","tempCurrentIndex","nextTempStart","nextTempEnd","childHTML","childNode","parseHTMLFragment","fragmentNodes","Node","htmlTrimmed","singleNodeData","fragmentNodeData","childData","#generateHTMLFromData","styleStr","child","newNode","nodeToInsert","#convertToNode","position","index","attrName","styleProp","camelProp","styles","safeVal","node"],"mappings":"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"}
@@ -0,0 +1,381 @@
1
+ const C = ["img", "br", "input", "meta", "link", "hr", "area", "base", "col", "embed", "param", "source", "track", "wbr"];
2
+ function M(o) {
3
+ return o.replace(/[A-Z]/g, (t) => `-${t.toLowerCase()}`);
4
+ }
5
+ function S(o) {
6
+ return o.replace(/-([a-z])/g, (t, e) => e.toUpperCase());
7
+ }
8
+ function A(o) {
9
+ const t = {};
10
+ if (!o) return t;
11
+ const e = /([a-zA-Z0-9-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/g;
12
+ let n;
13
+ for (; (n = e.exec(o)) !== null; ) {
14
+ const [, s, r, l, a] = n, h = r || l || a || "";
15
+ t[s] = h;
16
+ }
17
+ if (t.style) {
18
+ const s = {}, r = /([a-zA-Z0-9-]+)\s*:\s*([^;]+)/g;
19
+ let l;
20
+ for (; (l = r.exec(t.style)) !== null; ) {
21
+ const [, a, h] = l;
22
+ s[S(a.trim())] = h.trim();
23
+ }
24
+ t.styleObj = s, delete t.style;
25
+ }
26
+ return t;
27
+ }
28
+ function g(o) {
29
+ if (o = o.trim(), !o) return null;
30
+ if (!o.startsWith("<"))
31
+ return {
32
+ tagName: "#text",
33
+ textContent: o,
34
+ attributes: {},
35
+ // 符合IAttributeData类型
36
+ styles: {},
37
+ children: [],
38
+ parent: null
39
+ };
40
+ const t = /<([a-zA-Z0-9]+)\s*(.*?)\/?>/, e = o.match(t);
41
+ if (e) {
42
+ const [, c, i] = e;
43
+ if (C.includes(c.toLowerCase())) {
44
+ const N = A(i);
45
+ return {
46
+ tagName: c.toLowerCase(),
47
+ attributes: N,
48
+ // 类型匹配
49
+ styles: N.styleObj || {},
50
+ // styleObj是Record<string, string>,符合styles类型
51
+ textContent: "",
52
+ children: [],
53
+ parent: null
54
+ };
55
+ }
56
+ }
57
+ const n = /<([a-zA-Z0-9]+)\s*(.*?)>/, s = o.match(n);
58
+ if (!s) return null;
59
+ const [r, l, a] = s, h = l.toLowerCase(), u = `</${h}>`;
60
+ let x = -1, d = 1, m = r.length;
61
+ for (; m < o.length && d > 0; ) {
62
+ const c = o.indexOf(`<${h}`, m), i = o.indexOf(u, m);
63
+ if (i === -1) break;
64
+ c !== -1 && c < i ? (d++, m = c + `<${h}`.length) : (d--, d === 0 && (x = i), m = i + u.length);
65
+ }
66
+ const L = x !== -1 ? o.slice(r.length, x).trim() : o.slice(r.length).trim(), I = A(a), w = {
67
+ tagName: h,
68
+ attributes: I,
69
+ // 类型匹配
70
+ styles: I.styleObj || {},
71
+ // 类型匹配
72
+ textContent: "",
73
+ children: [],
74
+ parent: null
75
+ };
76
+ if (L) {
77
+ const c = [];
78
+ let i = L;
79
+ for (; i; ) {
80
+ const N = i.indexOf("<");
81
+ if (N === -1) {
82
+ const p = g(i);
83
+ p && c.push(p), i = "";
84
+ } else if (N > 0) {
85
+ const p = g(i.slice(0, N));
86
+ p && c.push(p), i = i.slice(N);
87
+ } else {
88
+ const p = i.match(n);
89
+ if (!p) {
90
+ const f = g(i);
91
+ f && c.push(f), i = "";
92
+ continue;
93
+ }
94
+ const E = p[1].toLowerCase(), $ = `</${E}>`;
95
+ if (C.includes(E)) {
96
+ const f = g(i.slice(0, p[0].length));
97
+ f && c.push(f), i = i.slice(p[0].length).trim();
98
+ continue;
99
+ }
100
+ let O = -1, j = 1, b = p[0].length;
101
+ for (; b < i.length && j > 0; ) {
102
+ const f = i.indexOf(`<${E}`, b), y = i.indexOf($, b);
103
+ if (y === -1) break;
104
+ f !== -1 && f < y ? (j++, b = f + `<${E}`.length) : (j--, j === 0 && (O = y), b = y + $.length);
105
+ }
106
+ if (O !== -1) {
107
+ const f = i.slice(0, O + $.length), y = g(f);
108
+ y && c.push(y), i = i.slice(O + $.length).trim();
109
+ } else {
110
+ const f = g(i);
111
+ f && c.push(f), i = "";
112
+ }
113
+ }
114
+ }
115
+ w.children = c.filter(Boolean), w.children.length === 1 && w.children[0].tagName === "#text" && (w.textContent = w.children[0].textContent, w.children = []);
116
+ }
117
+ return w;
118
+ }
119
+ function k(o) {
120
+ if (o = o.trim(), !o) return [];
121
+ const t = [];
122
+ let e = o;
123
+ for (; e; ) {
124
+ const n = e.indexOf("<");
125
+ if (n === -1) {
126
+ const s = g(e);
127
+ s && t.push(s), e = "";
128
+ } else if (n > 0) {
129
+ const s = g(e.slice(0, n));
130
+ s && t.push(s), e = e.slice(n);
131
+ } else {
132
+ const s = /<([a-zA-Z0-9]+)\s*(.*?)>/, r = e.match(s);
133
+ if (!r) {
134
+ const a = g(e);
135
+ a && t.push(a), e = "";
136
+ continue;
137
+ }
138
+ const l = r[1].toLowerCase();
139
+ if (C.includes(l)) {
140
+ const a = /<([a-zA-Z0-9]+)\s*(.*?)\/?>/, h = e.match(a);
141
+ if (h) {
142
+ const u = g(h[0]);
143
+ u && t.push(u), e = e.slice(h[0].length).trim();
144
+ } else {
145
+ const u = g(e);
146
+ u && t.push(u), e = "";
147
+ }
148
+ } else {
149
+ const a = `</${l}>`;
150
+ let h = -1, u = 1, x = r[0].length;
151
+ for (; x < e.length && u > 0; ) {
152
+ const d = e.indexOf(`<${l}`, x), m = e.indexOf(a, x);
153
+ if (m === -1) break;
154
+ d !== -1 && d < m ? (u++, x = d + `<${l}`.length) : (u--, u === 0 && (h = m), x = m + a.length);
155
+ }
156
+ if (h !== -1) {
157
+ const d = e.slice(0, h + a.length), m = g(d);
158
+ m && t.push(m), e = e.slice(h + a.length).trim();
159
+ } else {
160
+ const d = g(e);
161
+ d && t.push(d), e = "";
162
+ }
163
+ }
164
+ }
165
+ }
166
+ return t.filter(Boolean);
167
+ }
168
+ class T {
169
+ /**
170
+ * 构造函数:通过HTML字符串初始化节点(支持单根/多根)
171
+ * @param {string} html - HTML字符串(单根/多根均可)
172
+ */
173
+ constructor(t) {
174
+ if (typeof t != "string")
175
+ throw new Error("初始化Node必须传入HTML字符串");
176
+ const e = t.trim();
177
+ if (!e)
178
+ throw new Error("无法解析空的HTML字符串");
179
+ const n = g(e), s = k(e);
180
+ if (this.tagName = "", this.attributes = {}, this.styles = {}, this.textContent = "", this.children = [], this.parent = null, n && s.length === 1)
181
+ this.tagName = n.tagName, this.attributes = { ...n.attributes }, this.styles = { ...n.styles }, this.textContent = n.textContent || "", n.children.length > 0 && (this.children = n.children.map((r) => {
182
+ const l = new T(this.#t(r));
183
+ return l.parent = this, l;
184
+ }));
185
+ else if (s.length > 0)
186
+ this.tagName = "#fragment", this.attributes = {}, this.styles = {}, this.textContent = "", this.children = s.map((r) => {
187
+ const l = new T(this.#t(r));
188
+ return l.parent = this, l;
189
+ });
190
+ else
191
+ throw new Error("无法解析无效的HTML字符串");
192
+ }
193
+ /**
194
+ * 私有方法:从节点数据生成HTML字符串(用于子节点初始化)
195
+ * @param {INodeData} nodeData - 节点数据
196
+ * @returns {string} HTML字符串
197
+ */
198
+ #t(t) {
199
+ if (t.tagName === "#text") return t.textContent;
200
+ let e = `<${t.tagName}`;
201
+ const n = { ...t.attributes };
202
+ if (Object.keys(t.styles).length > 0) {
203
+ const r = Object.entries(t.styles).map(([l, a]) => `${M(l)}: ${a}`).join("; ");
204
+ n.style = r;
205
+ }
206
+ for (const [r, l] of Object.entries(n))
207
+ r !== "styleObj" && typeof l == "string" && (e += ` ${r}="${l}"`);
208
+ if (C.includes(t.tagName))
209
+ return e += "/>", e;
210
+ e += ">";
211
+ let s = t.textContent;
212
+ return t.children.length > 0 && (s += t.children.map((r) => this.#t(r)).join("")), `${e}${s}</${t.tagName}>`;
213
+ }
214
+ /**
215
+ * 子集管理:获取当前节点的所有子节点
216
+ * @returns {Node[]} 子节点数组(浅拷贝)
217
+ */
218
+ child() {
219
+ return [...this.children];
220
+ }
221
+ /**
222
+ * DOM操作:在当前节点之后插入新元素
223
+ * @param {string|Node} newNode - 要插入的HTML字符串或Node实例
224
+ * @returns {Node} 当前节点(链式调用)
225
+ */
226
+ before(t) {
227
+ if (!this.parent)
228
+ throw new Error("当前节点没有父节点,无法执行before操作");
229
+ const e = this.#e(t), n = this.parent.children.findIndex((s) => s === this);
230
+ if (n === -1)
231
+ throw new Error("当前节点不在父节点的子节点列表中");
232
+ return this.parent.children.splice(n, 0, e), e.parent = this.parent, this;
233
+ }
234
+ /**
235
+ * DOM操作:在当前节点之后插入新元素
236
+ * @param {string|Node} newNode - 要插入的HTML字符串或Node实例
237
+ * @returns {Node} 当前节点(链式调用)
238
+ */
239
+ after(t) {
240
+ if (!this.parent)
241
+ throw new Error("当前节点没有父节点,无法执行after操作");
242
+ const e = this.#e(t), n = this.parent.children.findIndex((s) => s === this);
243
+ if (n === -1)
244
+ throw new Error("当前节点不在父节点的子节点列表中");
245
+ return this.parent.children.splice(n + 1, 0, e), e.parent = this.parent, this;
246
+ }
247
+ /**
248
+ * DOM操作:在指定位置插入新元素
249
+ * @param {number} position - 插入位置(0 ~ children.length)
250
+ * @param {string|Node} newNode - 要插入的HTML字符串或Node实例
251
+ * @returns {Node} 当前节点(链式调用)
252
+ */
253
+ insert(t, e) {
254
+ if (typeof t != "number" || t < 0 || t > this.children.length)
255
+ throw new Error(`插入位置${t}无效,必须是0到${this.children.length}之间的整数`);
256
+ const n = this.#e(e);
257
+ return n.tagName === "#fragment" ? n.children.forEach((s, r) => {
258
+ s.parent = this, this.children.splice(t + r, 0, s);
259
+ }) : (this.children.splice(t, 0, n), n.parent = this), this;
260
+ }
261
+ /**
262
+ * 属性操作:获取指定属性的值
263
+ * @param {string} attrName - 属性名
264
+ * @returns {string|null} 属性值(不存在返回null)
265
+ */
266
+ getAttr(t) {
267
+ if (this.tagName === "#fragment" || this.tagName === "#text")
268
+ return null;
269
+ if (typeof t != "string")
270
+ throw new Error("属性名必须是字符串");
271
+ return typeof this.attributes[t] == "string" ? this.attributes[t] : null;
272
+ }
273
+ /**
274
+ * 属性操作:设置指定属性的值
275
+ * @param {string} attrName - 属性名
276
+ * @param {string|null|undefined} value - 属性值(null/undefined删除属性)
277
+ * @returns {Node} 当前节点(链式调用)
278
+ */
279
+ setAttr(t, e) {
280
+ if (this.tagName === "#fragment" || this.tagName === "#text")
281
+ throw new Error("片段/文本节点不支持设置属性");
282
+ if (typeof t != "string")
283
+ throw new Error("属性名必须是字符串");
284
+ return e == null ? delete this.attributes[t] : this.attributes[t] = String(e), this;
285
+ }
286
+ /**
287
+ * 属性操作:批量设置多个属性
288
+ * @param {Record<string, string | null | undefined>} attrs - 包含属性名-属性值键值对的对象
289
+ * @returns {Node} 当前节点(链式调用)
290
+ */
291
+ setAttrs(t) {
292
+ if (this.tagName === "#fragment" || this.tagName === "#text")
293
+ throw new Error("片段/文本节点不支持设置属性");
294
+ if (typeof t != "object" || t === null)
295
+ throw new Error("属性对象必须是非空对象");
296
+ for (const [e, n] of Object.entries(t))
297
+ this.setAttr(e, n);
298
+ return this;
299
+ }
300
+ /**
301
+ * 样式操作:获取指定样式属性的值
302
+ * @param {string} styleProp - 样式属性名(支持驼峰/短横线)
303
+ * @returns {string|null} 样式值(不存在返回null)
304
+ */
305
+ getStyle(t) {
306
+ if (this.tagName === "#fragment" || this.tagName === "#text")
307
+ return null;
308
+ if (typeof t != "string")
309
+ throw new Error("样式属性名必须是字符串");
310
+ const e = S(t);
311
+ return this.styles[e] || this.styles[t] || null;
312
+ }
313
+ /**
314
+ * 样式操作:设置指定样式属性的值
315
+ * @param {string} styleProp - 样式属性名(支持驼峰/短横线)
316
+ * @param {string|null|undefined} value - 样式值(null/undefined删除样式)
317
+ * @returns {Node} 当前节点(链式调用)
318
+ */
319
+ setStyle(t, e) {
320
+ if (this.tagName === "#fragment" || this.tagName === "#text")
321
+ throw new Error("片段/文本节点不支持设置样式");
322
+ if (typeof t != "string")
323
+ throw new Error("样式属性名必须是字符串");
324
+ const n = S(t);
325
+ return e == null ? (delete this.styles[n], delete this.styles[t]) : this.styles[n] = String(e), this;
326
+ }
327
+ /**
328
+ * 样式操作:批量设置多个样式属性
329
+ * @param {Record<string, string | null | undefined>} styles - 包含样式属性名-样式值键值对的对象
330
+ * @returns {Node} 当前节点(链式调用)
331
+ */
332
+ setStyles(t) {
333
+ if (this.tagName === "#fragment" || this.tagName === "#text")
334
+ throw new Error("片段/文本节点不支持设置样式");
335
+ if (typeof t != "object" || t === null)
336
+ throw new Error("样式对象必须是非空对象");
337
+ for (const [e, n] of Object.entries(t))
338
+ this.setStyle(e, n);
339
+ return this;
340
+ }
341
+ /**
342
+ * 获取当前节点的完整HTML文本
343
+ * @returns {string} HTML字符串
344
+ */
345
+ getHtml() {
346
+ if (this.tagName === "#text")
347
+ return this.textContent;
348
+ if (this.tagName === "#fragment")
349
+ return this.children.map((s) => s.getHtml()).join("");
350
+ let t = `<${this.tagName}`;
351
+ const e = { ...this.attributes };
352
+ if (Object.keys(this.styles).length > 0) {
353
+ const s = Object.entries(this.styles).map(([r, l]) => `${M(r)}: ${l}`).join("; ");
354
+ e.style = s;
355
+ }
356
+ for (const [s, r] of Object.entries(e))
357
+ if (s !== "styleObj" && typeof r == "string") {
358
+ const l = r.replace(/"/g, "&quot;");
359
+ t += ` ${s}="${l}"`;
360
+ }
361
+ if (C.includes(this.tagName))
362
+ return t += "/>", t;
363
+ t += ">";
364
+ let n = this.textContent;
365
+ return this.children.length > 0 && (n += this.children.map((s) => s.getHtml()).join("")), `${t}${n}</${this.tagName}>`;
366
+ }
367
+ /**
368
+ * 私有辅助方法:统一转换插入的节点为Node实例
369
+ * @param {string|Node} node - HTML字符串或Node实例
370
+ * @returns {Node} Node实例
371
+ */
372
+ #e(t) {
373
+ if (t instanceof T) return t;
374
+ if (typeof t == "string") return new T(t);
375
+ throw new Error("插入的节点必须是HTML字符串或Node实例");
376
+ }
377
+ }
378
+ export {
379
+ T as default
380
+ };
381
+ //# sourceMappingURL=parse_html.es.js.map