flex-html-render 1.0.3 → 1.1.1

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
@@ -34,8 +34,17 @@ const htmlString = `
34
34
 
35
35
  const flexMessage = convertHtmlToFlexMessage(htmlString);
36
36
  console.log(JSON.stringify(flexMessage, null, 2));
37
+
38
+ // 將產生的 JSON 放入 LINE Messaging API 的訊息結構中
39
+ const message = {
40
+ type: 'flex',
41
+ altText: 'Flex Message',
42
+ contents: flexMessage[0] // 因為 convertHtmlToFlexMessage 回傳陣列,取第一個元素
43
+ };
37
44
  ```
38
45
 
46
+ **注意**:此套件產生的 JSON 物件應放置在 LINE Messaging API 中 `type` 為 `flex` 的訊息物件的 `contents` 區塊內。詳細請參考 [LINE Flex Message 官方文件](https://developers.line.biz/en/reference/messaging-api/#flex-message)。
47
+
39
48
  ## API
40
49
 
41
50
  ### `convertHtmlToFlexMessage(htmlString)`
@@ -98,6 +107,7 @@ console.log(JSON.stringify(flexMessage, null, 2));
98
107
  ### 特殊功能標籤
99
108
  - `<action>` - 動作設定標籤,必須放在需要添加動作的元件內(如 `<button>`、`<box>`、`<image>` 等)
100
109
  - `<background>` - 背景設定標籤,只能放在 Box 相關標籤內(`<box>`、`<baseline>`、`<row>`、`<vertical>`)
110
+ - `<style>` - 樣式設定標籤,只能放在 top-level 區塊標籤內(`<header>`、`<hero>`、`<body>`、`<footer>`)
101
111
 
102
112
  ## 特殊標籤使用規則
103
113
 
@@ -171,7 +181,26 @@ console.log(JSON.stringify(flexMessage, null, 2));
171
181
  </text>
172
182
  ```
173
183
 
174
- ### 4. `<strong>` 使用方式
184
+ ### 4. `<style>` 使用規則
185
+ `<style>` 標籤只能放在 top-level 區塊標籤內(`<header>`、`<hero>`、`<body>`、`<footer>`),用於設定該區塊的樣式屬性,如背景色等。各區塊標籤只能有一個 `<style>` 子元素。最終會整合為一個 styles 屬性到 bubble 的 JSON 結構中。
186
+
187
+ ```xml
188
+ <!-- ✅ 正確 - 在 header 內使用 -->
189
+ <header>
190
+ <style backgroundColor="#ff0000" />
191
+ <box layout="vertical">
192
+ <text>有背景色的標題</text>
193
+ </box>
194
+ </header>
195
+
196
+ <!-- ❌ 錯誤 - 不能在 box 內使用 -->
197
+ <box layout="vertical">
198
+ <style backgroundColor="#ff0000" />
199
+ <text>文字</text>
200
+ </box>
201
+ ```
202
+
203
+ ### 5. `<strong>` 使用方式
175
204
  `<strong>` 標籤會自動轉換為 `<text weight="bold">`,用於顯示粗體文字。
176
205
 
177
206
  ```html
@@ -181,7 +210,7 @@ console.log(JSON.stringify(flexMessage, null, 2));
181
210
  </box>
182
211
  ```
183
212
 
184
- ### 5. `<space>` 使用方式
213
+ ### 6. `<space>` 使用方式
185
214
  `<space>` 標籤用於在文字中插入空格,可透過 `size` 屬性指定空格數量。
186
215
 
187
216
  ```html
@@ -192,7 +221,7 @@ console.log(JSON.stringify(flexMessage, null, 2));
192
221
  </text>
193
222
  ```
194
223
 
195
- ### 6. 便捷 Box 標籤(`<baseline>`、`<row>`、`<vertical>`)
224
+ ### 7. 便捷 Box 標籤(`<baseline>`、`<row>`、`<vertical>`)
196
225
  這些標籤是 `<box>` 的簡寫形式,省去了 `layout` 屬性的設定。
197
226
 
198
227
  ```html
@@ -215,7 +244,7 @@ console.log(JSON.stringify(flexMessage, null, 2));
215
244
  </vertical>
216
245
  ```
217
246
 
218
- ### 7. `<carousel>` 使用規則
247
+ ### 8. `<carousel>` 使用規則
219
248
  `<carousel>` 只能包含 `<bubble>` 子元素,且最多 12 個。
220
249
 
221
250
  ```html
@@ -237,7 +266,7 @@ console.log(JSON.stringify(flexMessage, null, 2));
237
266
  </carousel>
238
267
  ```
239
268
 
240
- ### 8. `<video>` 使用規則
269
+ ### 9. `<video>` 使用規則
241
270
  `<video>` 必須包含一個 `<box>` 或 `<image>` 作為 altContent(影片無法播放時的替代內容)。
242
271
 
243
272
  ```html
@@ -248,9 +277,10 @@ console.log(JSON.stringify(flexMessage, null, 2));
248
277
 
249
278
  ## 完整範例
250
279
 
251
- ```html
280
+ ```xml
252
281
  <bubble>
253
282
  <header>
283
+ <style backgroundColor="#f0f0f0" />
254
284
  <vertical>
255
285
  <background type="linearGradient" angle="90deg" startColor="#667eea" endColor="#764ba2" />
256
286
  <strong>商品資訊</strong>
@@ -1,6 +1,6 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const J=require("htmlparser2"),E={ELEMENT:"element",TEXT:"text"},e={CAROUSEL:"carousel",BUBBLE:"bubble",HEADER:"header",HERO:"hero",BODY:"body",FOOTER:"footer",BOX:"box",TEXT:"text",SPAN:"span",IMAGE:"image",VIDEO:"video",ICON:"icon",SEPARATOR:"separator",BUTTON:"button",FILLER:"filler",SPACE:"space",STRONG:"strong",BASELINE:"baseline",ROW:"row",VERTICAL:"vertical",DIV:"div",ARTICLE:"article",ACTION:"action",BACKGROUND:"background"},Y=[e.HEADER,e.HERO,e.BODY,e.FOOTER],W=[e.BOX,e.VERTICAL,e.ROW,e.BASELINE,e.DIV,e.ARTICLE],q=[e.TEXT,e.STRONG];function z(t){const r=Number(t);return isNaN(r)?t:r}function _(t){return t==="true"?!0:t==="false"?!1:t}function Q(t){return[z,_].reduce((n,s)=>s(n),t)}function Z(t){const r={type:"root",children:[]},n=[r],s=new J.Parser({onopentag(l,i){const a={type:E.ELEMENT,tagName:l,attributes:Object.fromEntries(Object.entries(i).map(([p,f])=>[p,Q(f)])),children:[]};n[n.length-1].children.push(a),n.push(a)},ontext(l){const i=n[n.length-1];if(i.tagName==="span"){i.children.push({type:E.TEXT,content:l});return}l.trim()&&i.children.push({type:E.TEXT,content:l.replace(/\n/g,"").trim()})},onclosetag(){n.pop()},onerror(l){console.error("解析錯誤:",l)}},{xmlMode:!0,lowerCaseTags:!1,lowerCaseAttributeNames:!1});return s.write(t),s.end(),r.children}const h=t=>{throw Error(`[Flex Html Render]: ${t}`)},c={carousel:t=>({type:"carousel",contents:t}),bubble:(t={})=>({type:"bubble",...t}),box:(t,r={})=>({type:"box",contents:t,...r}),text:(t,r={})=>({type:"text",...typeof t=="string"?{text:t}:{contents:t},...r}),span:(t,r={})=>({type:"span",text:t,...r}),image:(t={})=>({type:"image",...t}),video:(t={})=>({type:"video",...t}),icon:(t={})=>({type:"icon",...t}),button:(t={})=>({type:"button",...t}),separator:(t={color:"#E0E3EA"})=>({type:"separator",...t}),filler:(t={})=>({type:"filler",...t}),uri:(t={})=>({type:"uri",altUri:t.altUri?{desktop:t.altUri}:void 0,...t}),postback:(t={})=>({type:"postback",...t}),message:(t={})=>({type:"message",...t}),datetimepicker:(t={})=>({type:"datetimepicker",...t}),camera:(t={})=>({type:"camera",...t}),cameraRoll:(t={})=>({type:"cameraRoll",...t}),location:(t={})=>({type:"location",...t}),richmenuswitch:(t={})=>({type:"richmenuswitch",...t}),clipboard:(t={})=>({type:"clipboard",...t})};function tt(t){if(t.tagName!==e.BACKGROUND&&t.children&&t.children.length>0){const r=t.children.filter(n=>n.tagName===e.BACKGROUND);return r.length>1&&h("A box node can only allowed with one background node inside"),t.children=t.children.filter(n=>n.tagName!==e.BACKGROUND),r[0]}return null}function et(t){if(t.tagName!==e.ACTION&&t.children&&t.children.length>0){const r=t.children.filter(n=>n.tagName===e.ACTION);return r.length>1&&h("A node can only allowed with one action node inside"),t.children=t.children.filter(n=>n.tagName!==e.ACTION),r[0]}return null}function rt(t){![...q,e.SPAN].includes(t.tagName)&&t.children?.some(l=>l.type===E.TEXT)&&(console.log(t),h("Span component only allowed inside Text component")),t.tagName===e.VIDEO&&t.children?.some(l=>![e.BOX,e.IMAGE].includes(l.tagName))&&h("Video component only allowed Box or Image as altContent child"),!W.includes(t.tagName)&&t.children?.some(l=>l.tagName===e.BACKGROUND)&&h("Only Box component allowed to have Background child")}function m(t){t.attributes=t.attributes||{},rt(t);const r=et(t);r&&(t.attributes.action=m(r));const n=tt(t);if(n&&(t.attributes.background=m(n)),t.type===E.TEXT)return c.span(t.content,t.attributes);if(t.tagName===e.BUBBLE){const s=t.children.find(u=>u.tagName===e.HEADER),l=t.children.find(u=>u.tagName===e.HERO),i=t.children.find(u=>u.tagName===e.BODY),a=t.children.find(u=>u.tagName===e.FOOTER);return c.bubble({...t.attributes,header:s?m(s):void 0,hero:l?m(l):void 0,body:i?m(i):void 0,footer:a?m(a):void 0})}if(t.tagName===e.CAROUSEL)return t.children.some(s=>s.tagName!==e.BUBBLE)&&h("Carousel can only have Bubble as children"),t.children.length>12&&h("Carousel can have maximum 12 bubbles"),c.carousel(t.children.map(m));if(Y.includes(t.tagName))return t.children.length!==1&&h(`${t.tagName} should have exactly one child node`),m(t.children[0]);if(t.tagName===e.BUTTON)return t.attributes.action||h("Button component should contain an action node"),c.button(t.attributes);if(t.tagName===e.SPAN){t.children.length>1&&h("Span component can only have one text child");const s=t.children[0];if(s&&s.type===E.TEXT)return c.span(s.content,t.attributes);h(s?"Span component only allowed text child":"Span component can not be empty")}if(t.tagName===e.TEXT)return c.text(t.children.map(m),t.attributes);if(t.tagName===e.SEPARATOR)return c.separator(t.attributes);if(t.tagName===e.IMAGE)return c.image(t.attributes);if(t.tagName===e.VIDEO)return t.children.length!==1&&h("Video component should only has one Box or Image as altContent"),t.attributes.altContent=m(t.children[0]),c.video(t.attributes);if(t.tagName===e.ICON)return c.icon(t.attributes);if(t.tagName===e.FILLER)return c.filler(t.attributes);if(t.tagName===e.BOX||t.tagName===e.DIV||t.tagName===e.ARTICLE)return t.attributes.layout||(t.attributes.layout="vertical"),c.box(t.children.map(m),t.attributes);if(t.tagName===e.SPACE)return c.span(" ".repeat(t.attributes.size||1));if(t.tagName===e.STRONG)return t.tagName=e.TEXT,t.attributes.weight="bold",m(t);if(t.tagName===e.BASELINE)return t.tagName=e.BOX,t.attributes.layout="baseline",m(t);if(t.tagName===e.ROW)return t.tagName=e.BOX,t.attributes.layout="horizontal",m(t);if(t.tagName===e.VERTICAL)return t.tagName=e.BOX,t.attributes.layout="vertical",m(t);if(t.tagName===e.ACTION){const s=t.attributes.type;switch(s){case"uri":return c.uri(t.attributes);case"postback":return c.postback(t.attributes);case"message":return c.message(t.attributes);case"datetimepicker":return c.datetimepicker(t.attributes);case"camera":return c.camera(t.attributes);case"cameraRoll":return c.cameraRoll(t.attributes);case"location":return c.location(t.attributes);case"richmenuswitch":return c.richmenuswitch(t.attributes);case"clipboard":return c.clipboard(t.attributes);default:h(`Unsupported action type: ${s}`)}}if(t.tagName===e.BACKGROUND)return t.attributes}const o={createElement:(t,r={},n=[],s=0)=>{const i=Object.entries(r).filter(([u,p])=>p!=null).map(([u,p])=>typeof p=="object"?`${u}="${JSON.stringify(p).replace(/"/g,"&quot;")}"`:`${u}="${p}"`).join(" "),a=i?` ${i}`:"";if(n.length===0)return` <${t}${a} />`;{const u=n.map(p=>typeof p=="string"?p.split(`
2
- `).map(f=>f?" "+f:"").join(`
3
- `):p).join(`
4
- `);return` <${t}${a}>
5
- ${u}
6
- </${t}>`}},createText:t=>t};function b(t,r=0){if(!t||typeof t!="object")return"";const{type:n,...s}=t,{action:l,...i}=s,a=l?b(l,r+1):"";switch(n){case"carousel":const{contents:u,...p}=i,f=u.map(N=>b(N,r+1));return a&&f.unshift(a),o.createElement(e.CAROUSEL,p,f,r);case"bubble":const{header:A,hero:C,body:R,footer:B,...L}=i,g=[];return a&&g.push(a),A&&g.push(o.createElement(e.HEADER,{},[b(A,r+1)],r+1)),C&&g.push(o.createElement(e.HERO,{},[b(C,r+1)],r+1)),R&&g.push(o.createElement(e.BODY,{},[b(R,r+1)],r+1)),B&&g.push(o.createElement(e.FOOTER,{},[b(B,r+1)],r+1)),o.createElement(e.BUBBLE,L,g,r);case"box":const{contents:w,background:d,...O}=i;O.layout||(O.layout="vertical");const T=[];return a&&T.push(a),d&&T.push(b({type:"background",...d},r+1)),T.push(...(w||[]).map(N=>b(N,r+1))),o.createElement(e.BOX,O,T,r);case"text":const{text:U,contents:y,...k}=i,x=y&&y.length>0?y.map(N=>b(N,r+1)):[U||""];return a&&x.unshift(a),o.createElement(e.TEXT,k,x,r);case"span":const{text:D,...X}=i,S=[D||""];return a&&S.unshift(a),o.createElement(e.SPAN,X,S,r);case"image":const{...$}=i,H=a?[a]:[];return o.createElement(e.IMAGE,$,H,r);case"video":const{altContent:I,...G}=i,v=I?[b(I,r+1)]:[];return a&&v.unshift(a),o.createElement(e.VIDEO,G,v,r);case"icon":const{...M}=i,F=a?[a]:[];return o.createElement(e.ICON,M,F,r);case"button":const{...P}=i,V=a?[a]:[];return o.createElement(e.BUTTON,P,V,r);case"separator":const j=a?[a]:[];return o.createElement(e.SEPARATOR,i,j,r);case"filler":const K=a?[a]:[];return o.createElement(e.FILLER,i,K,r);case"uri":case"message":case"postback":case"datetimepicker":case"camera":case"cameraRoll":case"location":case"richmenuswitch":case"clipboard":return o.createElement(e.ACTION,{type:n,...i},[],r);case"background":return o.createElement(e.BACKGROUND,i,[],r);default:return""}}function at(t){try{return Z(t).map(m)}catch(r){throw console.error("Error converting HTML to Flex Message:",r),r}}exports.convertJsonToHtml=b;exports.default=at;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const z=require("htmlparser2"),E={ELEMENT:"element",TEXT:"text"},e={CAROUSEL:"carousel",BUBBLE:"bubble",HEADER:"header",HERO:"hero",BODY:"body",FOOTER:"footer",BOX:"box",TEXT:"text",SPAN:"span",IMAGE:"image",VIDEO:"video",ICON:"icon",SEPARATOR:"separator",BUTTON:"button",FILLER:"filler",SPACE:"space",STRONG:"strong",BASELINE:"baseline",ROW:"row",VERTICAL:"vertical",DIV:"div",ARTICLE:"article",ACTION:"action",BACKGROUND:"background",STYLE:"style"},_=[e.HEADER,e.HERO,e.BODY,e.FOOTER],Q=[e.BOX,e.VERTICAL,e.ROW,e.BASELINE,e.DIV,e.ARTICLE],Z=[e.TEXT,e.STRONG];function tt(t){const r=Number(t);return isNaN(r)?t:r}function et(t){return t==="true"?!0:t==="false"?!1:t}function rt(t){return[tt,et].reduce((a,c)=>c(a),t)}function at(t){const r={type:"root",children:[]},a=[r],c=new z.Parser({onopentag(s,i){const n={type:E.ELEMENT,tagName:s,attributes:Object.fromEntries(Object.entries(i).map(([l,b])=>[l,rt(b)])),children:[]};a[a.length-1].children.push(n),a.push(n)},ontext(s){const i=a[a.length-1];if(i.tagName==="span"){i.children.push({type:E.TEXT,content:s});return}s.trim()&&i.children.push({type:E.TEXT,content:s.replace(/\n/g,"").trim()})},onclosetag(){a.pop()},onerror(s){console.error("解析錯誤:",s)}},{xmlMode:!0,lowerCaseTags:!1,lowerCaseAttributeNames:!1});return c.write(t),c.end(),r.children}const h=t=>{throw Error(`[Flex Html Render]: ${t}`)},o={carousel:t=>({type:"carousel",contents:t}),bubble:(t={})=>({type:"bubble",...t}),box:(t,r={})=>({type:"box",contents:t,...r}),text:(t,r={})=>({type:"text",...typeof t=="string"?{text:t}:{contents:t},...r}),span:(t,r={})=>({type:"span",text:t,...r}),image:(t={})=>({type:"image",...t}),video:(t={})=>({type:"video",...t}),icon:(t={})=>({type:"icon",...t}),button:(t={})=>({type:"button",...t}),separator:(t={color:"#E0E3EA"})=>({type:"separator",...t}),filler:(t={})=>({type:"filler",...t}),uri:(t={})=>({type:"uri",altUri:t.altUri?{desktop:t.altUri}:void 0,...t}),postback:(t={})=>({type:"postback",...t}),message:(t={})=>({type:"message",...t}),datetimepicker:(t={})=>({type:"datetimepicker",...t}),camera:(t={})=>({type:"camera",...t}),cameraRoll:(t={})=>({type:"cameraRoll",...t}),location:(t={})=>({type:"location",...t}),richmenuswitch:(t={})=>({type:"richmenuswitch",...t}),clipboard:(t={})=>({type:"clipboard",...t})};function nt(t){if(t.tagName!==e.BACKGROUND&&t.children&&t.children.length>0){const r=t.children.filter(a=>a.tagName===e.BACKGROUND);return r.length>1&&h("A box node can only allowed with one background node inside"),t.children=t.children.filter(a=>a.tagName!==e.BACKGROUND),r[0]}return null}function it(t){if(t.tagName!==e.ACTION&&t.children&&t.children.length>0){const r=t.children.filter(a=>a.tagName===e.ACTION);return r.length>1&&h("A node can only allowed with one action node inside"),t.children=t.children.filter(a=>a.tagName!==e.ACTION),r[0]}return null}function ct(t){![...Z,e.SPAN].includes(t.tagName)&&t.children?.some(s=>s.type===E.TEXT)&&h("Span component only allowed inside Text component"),t.tagName===e.VIDEO&&t.children?.some(s=>![e.BOX,e.IMAGE].includes(s.tagName))&&h("Video component only allowed Box or Image as altContent child"),!Q.includes(t.tagName)&&t.children?.some(s=>s.tagName===e.BACKGROUND)&&h("Only Box component allowed to have Background child")}function O(t){if(t){const r=t.children.find(a=>a.tagName===e.STYLE);if(r)return t.children=t.children.filter(a=>a.tagName!==e.STYLE),r.attributes}}function u(t){t.attributes=t.attributes||{},ct(t);const r=it(t);r&&(t.attributes.action=u(r));const a=nt(t);if(a&&(t.attributes.background=u(a)),t.type===E.TEXT)return o.span(t.content,t.attributes);if(t.tagName===e.BUBBLE){const c=t.children.find(l=>l.tagName===e.HEADER),s=t.children.find(l=>l.tagName===e.HERO),i=t.children.find(l=>l.tagName===e.BODY),n=t.children.find(l=>l.tagName===e.FOOTER),p={header:O(c),hero:O(s),body:O(i),footer:O(n)};return o.bubble({...t.attributes,header:c?u(c):void 0,hero:s?u(s):void 0,body:i?u(i):void 0,footer:n?u(n):void 0,styles:Object.values(p).some(l=>l!==void 0)?p:void 0})}if(t.tagName===e.CAROUSEL)return t.children.some(c=>c.tagName!==e.BUBBLE)&&h("Carousel can only have Bubble as children"),t.children.length>12&&h("Carousel can have maximum 12 bubbles"),o.carousel(t.children.map(u));if(_.includes(t.tagName))return t.children.length!==1&&h(`${t.tagName} should have exactly one child node`),u(t.children[0]);if(t.tagName===e.BUTTON)return t.attributes.action||h("Button component should contain an action node"),o.button(t.attributes);if(t.tagName===e.SPAN){t.children.length>1&&h("Span component can only have one text child");const c=t.children[0];if(c&&c.type===E.TEXT)return o.span(c.content,t.attributes);h(c?"Span component only allowed text child":"Span component can not be empty")}if(t.tagName===e.TEXT)return o.text(t.children.map(u),t.attributes);if(t.tagName===e.SEPARATOR)return o.separator(t.attributes);if(t.tagName===e.IMAGE)return o.image(t.attributes);if(t.tagName===e.VIDEO)return t.children.length!==1&&h("Video component should only has one Box or Image as altContent"),t.attributes.altContent=u(t.children[0]),o.video(t.attributes);if(t.tagName===e.ICON)return o.icon(t.attributes);if(t.tagName===e.FILLER)return o.filler(t.attributes);if(t.tagName===e.BOX||t.tagName===e.DIV||t.tagName===e.ARTICLE)return t.attributes.layout||(t.attributes.layout="vertical"),o.box(t.children.map(u),t.attributes);if(t.tagName===e.SPACE)return o.span(" ".repeat(t.attributes.size||1));if(t.tagName===e.STRONG)return t.tagName=e.TEXT,t.attributes.weight="bold",u(t);if(t.tagName===e.BASELINE)return t.tagName=e.BOX,t.attributes.layout="baseline",u(t);if(t.tagName===e.ROW)return t.tagName=e.BOX,t.attributes.layout="horizontal",u(t);if(t.tagName===e.VERTICAL)return t.tagName=e.BOX,t.attributes.layout="vertical",u(t);if(t.tagName===e.ACTION){const c=t.attributes.type;switch(c){case"uri":return o.uri(t.attributes);case"postback":return o.postback(t.attributes);case"message":return o.message(t.attributes);case"datetimepicker":return o.datetimepicker(t.attributes);case"camera":return o.camera(t.attributes);case"cameraRoll":return o.cameraRoll(t.attributes);case"location":return o.location(t.attributes);case"richmenuswitch":return o.richmenuswitch(t.attributes);case"clipboard":return o.clipboard(t.attributes);default:h(`Unsupported action type: ${c}`)}}if(t.tagName===e.BACKGROUND)return t.attributes}const m={createElement:(t,r={},a=[],c=0)=>{const i=Object.entries(r).filter(([p,l])=>l!=null).map(([p,l])=>typeof l=="object"?`${p}="${JSON.stringify(l).replace(/"/g,"&quot;")}"`:`${p}="${l}"`).join(" "),n=i?` ${i}`:"";if(a.length===0)return` <${t}${n} />`;{const p=a.map(l=>typeof l=="string"?l.split(`
2
+ `).map(b=>b?" "+b:"").join(`
3
+ `):l).join(`
4
+ `);return` <${t}${n}>
5
+ ${p}
6
+ </${t}>`}},createText:t=>t},A=(t,r,a,c)=>{const s=[f(r,c+1)];return a&&Object.keys(a).length>0&&s.unshift(f(D("style",a),c+1)),m.createElement(t,{},s,c)},D=(t,r)=>({...r,type:t});function f(t,r=0){if(!t||typeof t!="object")return"";const{type:a,...c}=t,{action:s,...i}=c,n=s?f(s,r+1):"";switch(a){case"carousel":const{contents:p,...l}=i,b=p.map(N=>f(N,r+1));return n&&b.unshift(n),m.createElement(e.CAROUSEL,l,b,r);case"bubble":const{header:R,hero:B,body:S,footer:x,styles:T={},...k}=i,g=[];return n&&g.push(n),R&&g.push(A(e.HEADER,R,T.header,r+1)),B&&g.push(A(e.HERO,B,T.hero,r+1)),S&&g.push(A(e.BODY,S,T.body,r+1)),x&&g.push(A(e.FOOTER,x,T.footer,r+1)),m.createElement(e.BUBBLE,k,g,r);case"box":const{contents:X,background:I,...C}=i;C.layout||(C.layout="vertical");const y=[];return n&&y.push(n),I&&y.push(f(D("background",I),r+1)),y.push(...(X||[]).map(N=>f(N,r+1))),m.createElement(e.BOX,C,y,r);case"text":const{text:$,contents:d,...H}=i,v=d&&d.length>0?d.map(N=>f(N,r+1)):[$||""];return n&&v.unshift(n),m.createElement(e.TEXT,H,v,r);case"span":const{text:G,...M}=i,L=[G||""];return n&&L.unshift(n),m.createElement(e.SPAN,M,L,r);case"image":const{...F}=i,P=n?[n]:[];return m.createElement(e.IMAGE,F,P,r);case"video":const{altContent:w,...V}=i,U=w?[f(w,r+1)]:[];return n&&U.unshift(n),m.createElement(e.VIDEO,V,U,r);case"icon":const{...j}=i,Y=n?[n]:[];return m.createElement(e.ICON,j,Y,r);case"button":const{...K}=i,J=n?[n]:[];return m.createElement(e.BUTTON,K,J,r);case"separator":const W=n?[n]:[];return m.createElement(e.SEPARATOR,i,W,r);case"filler":const q=n?[n]:[];return m.createElement(e.FILLER,i,q,r);case"uri":case"message":case"postback":case"datetimepicker":case"camera":case"cameraRoll":case"location":case"richmenuswitch":case"clipboard":return m.createElement(e.ACTION,{type:a,...i},[],r);case"background":return m.createElement(e.BACKGROUND,i,[],r);case"style":return m.createElement(e.STYLE,i,[],r);default:return""}}function st(t){try{return at(t).map(u)}catch(r){throw console.error("Error converting HTML to Flex Message:",r),r}}exports.convertJsonToHtml=f;exports.default=st;
@@ -1,4 +1,4 @@
1
- import { Parser as Y } from "htmlparser2";
1
+ import { Parser as q } from "htmlparser2";
2
2
  const E = {
3
3
  ELEMENT: "element",
4
4
  TEXT: "text"
@@ -32,69 +32,70 @@ const E = {
32
32
  ARTICLE: "article",
33
33
  // custom utils tags
34
34
  ACTION: "action",
35
- BACKGROUND: "background"
36
- }, J = [
35
+ BACKGROUND: "background",
36
+ STYLE: "style"
37
+ }, Q = [
37
38
  e.HEADER,
38
39
  e.HERO,
39
40
  e.BODY,
40
41
  e.FOOTER
41
- ], W = [
42
+ ], Z = [
42
43
  e.BOX,
43
44
  e.VERTICAL,
44
45
  e.ROW,
45
46
  e.BASELINE,
46
47
  e.DIV,
47
48
  e.ARTICLE
48
- ], z = [
49
+ ], _ = [
49
50
  e.TEXT,
50
51
  e.STRONG
51
52
  ];
52
- function q(t) {
53
+ function tt(t) {
53
54
  const r = Number(t);
54
55
  return isNaN(r) ? t : r;
55
56
  }
56
- function Q(t) {
57
+ function et(t) {
57
58
  return t === "true" ? !0 : t === "false" ? !1 : t;
58
59
  }
59
- function Z(t) {
60
+ function rt(t) {
60
61
  return [
61
- q,
62
- Q
63
- ].reduce((n, s) => s(n), t);
62
+ tt,
63
+ et
64
+ ].reduce((a, c) => c(a), t);
64
65
  }
65
- function _(t) {
66
- const r = { type: "root", children: [] }, n = [r], s = new Y(
66
+ function at(t) {
67
+ const r = { type: "root", children: [] }, a = [r], c = new q(
67
68
  {
68
- onopentag(l, i) {
69
- const a = {
69
+ onopentag(s, i) {
70
+ const n = {
70
71
  type: E.ELEMENT,
71
- tagName: l,
72
+ tagName: s,
72
73
  attributes: Object.fromEntries(
73
- Object.entries(i).map(([p, f]) => [p, Z(f)])
74
+ Object.entries(i).map(([l, b]) => [l, rt(b)])
74
75
  ),
75
76
  children: []
76
77
  };
77
- n[n.length - 1].children.push(a), n.push(a);
78
+ a[a.length - 1].children.push(n), a.push(n);
78
79
  },
79
- ontext(l) {
80
- const i = n[n.length - 1];
80
+ ontext(s) {
81
+ const i = a[a.length - 1];
81
82
  if (i.tagName === "span") {
82
83
  i.children.push({
83
84
  type: E.TEXT,
84
- content: l
85
+ content: s
85
86
  });
86
87
  return;
87
88
  }
88
- l.trim() && i.children.push({
89
+ s.trim() && i.children.push({
89
90
  type: E.TEXT,
90
- content: l.replace(/\n/g, "").trim()
91
+ content: s.replace(/\n/g, "").trim()
91
92
  });
92
93
  },
93
94
  onclosetag() {
94
- n.pop();
95
+ a.pop();
95
96
  },
96
- onerror(l) {
97
- console.error("解析錯誤:", l);
97
+ onerror(s) {
98
+ console.error("解析錯誤:", s);
98
99
  }
99
100
  },
100
101
  {
@@ -105,11 +106,11 @@ function _(t) {
105
106
  lowerCaseAttributeNames: !1
106
107
  }
107
108
  );
108
- return s.write(t), s.end(), r.children;
109
+ return c.write(t), c.end(), r.children;
109
110
  }
110
111
  const h = (t) => {
111
112
  throw Error(`[Flex Html Render]: ${t}`);
112
- }, c = {
113
+ }, o = {
113
114
  carousel: (t) => ({
114
115
  type: "carousel",
115
116
  contents: t
@@ -199,173 +200,189 @@ const h = (t) => {
199
200
  ...t
200
201
  })
201
202
  };
202
- function tt(t) {
203
+ function nt(t) {
203
204
  if (t.tagName !== e.BACKGROUND && t.children && t.children.length > 0) {
204
- const r = t.children.filter((n) => n.tagName === e.BACKGROUND);
205
- return r.length > 1 && h("A box node can only allowed with one background node inside"), t.children = t.children.filter((n) => n.tagName !== e.BACKGROUND), r[0];
205
+ const r = t.children.filter((a) => a.tagName === e.BACKGROUND);
206
+ return r.length > 1 && h("A box node can only allowed with one background node inside"), t.children = t.children.filter((a) => a.tagName !== e.BACKGROUND), r[0];
206
207
  }
207
208
  return null;
208
209
  }
209
- function et(t) {
210
+ function it(t) {
210
211
  if (t.tagName !== e.ACTION && t.children && t.children.length > 0) {
211
- const r = t.children.filter((n) => n.tagName === e.ACTION);
212
- return r.length > 1 && h("A node can only allowed with one action node inside"), t.children = t.children.filter((n) => n.tagName !== e.ACTION), r[0];
212
+ const r = t.children.filter((a) => a.tagName === e.ACTION);
213
+ return r.length > 1 && h("A node can only allowed with one action node inside"), t.children = t.children.filter((a) => a.tagName !== e.ACTION), r[0];
213
214
  }
214
215
  return null;
215
216
  }
216
- function rt(t) {
217
- ![...z, e.SPAN].includes(t.tagName) && t.children?.some((l) => l.type === E.TEXT) && (console.log(t), h("Span component only allowed inside Text component")), t.tagName === e.VIDEO && t.children?.some((l) => ![e.BOX, e.IMAGE].includes(l.tagName)) && h("Video component only allowed Box or Image as altContent child"), !W.includes(t.tagName) && t.children?.some((l) => l.tagName === e.BACKGROUND) && h("Only Box component allowed to have Background child");
217
+ function ct(t) {
218
+ ![..._, e.SPAN].includes(t.tagName) && t.children?.some((s) => s.type === E.TEXT) && h("Span component only allowed inside Text component"), t.tagName === e.VIDEO && t.children?.some((s) => ![e.BOX, e.IMAGE].includes(s.tagName)) && h("Video component only allowed Box or Image as altContent child"), !Z.includes(t.tagName) && t.children?.some((s) => s.tagName === e.BACKGROUND) && h("Only Box component allowed to have Background child");
219
+ }
220
+ function O(t) {
221
+ if (t) {
222
+ const r = t.children.find((a) => a.tagName === e.STYLE);
223
+ if (r)
224
+ return t.children = t.children.filter((a) => a.tagName !== e.STYLE), r.attributes;
225
+ }
218
226
  }
219
- function m(t) {
220
- t.attributes = t.attributes || {}, rt(t);
221
- const r = et(t);
222
- r && (t.attributes.action = m(r));
223
- const n = tt(t);
224
- if (n && (t.attributes.background = m(n)), t.type === E.TEXT)
225
- return c.span(t.content, t.attributes);
227
+ function u(t) {
228
+ t.attributes = t.attributes || {}, ct(t);
229
+ const r = it(t);
230
+ r && (t.attributes.action = u(r));
231
+ const a = nt(t);
232
+ if (a && (t.attributes.background = u(a)), t.type === E.TEXT)
233
+ return o.span(t.content, t.attributes);
226
234
  if (t.tagName === e.BUBBLE) {
227
- const s = t.children.find((u) => u.tagName === e.HEADER), l = t.children.find((u) => u.tagName === e.HERO), i = t.children.find((u) => u.tagName === e.BODY), a = t.children.find((u) => u.tagName === e.FOOTER);
228
- return c.bubble({
235
+ const c = t.children.find((l) => l.tagName === e.HEADER), s = t.children.find((l) => l.tagName === e.HERO), i = t.children.find((l) => l.tagName === e.BODY), n = t.children.find((l) => l.tagName === e.FOOTER), p = {
236
+ header: O(c),
237
+ hero: O(s),
238
+ body: O(i),
239
+ footer: O(n)
240
+ };
241
+ return o.bubble({
229
242
  ...t.attributes,
230
- header: s ? m(s) : void 0,
231
- hero: l ? m(l) : void 0,
232
- body: i ? m(i) : void 0,
233
- footer: a ? m(a) : void 0
243
+ header: c ? u(c) : void 0,
244
+ hero: s ? u(s) : void 0,
245
+ body: i ? u(i) : void 0,
246
+ footer: n ? u(n) : void 0,
247
+ styles: Object.values(p).some((l) => l !== void 0) ? p : void 0
234
248
  });
235
249
  }
236
250
  if (t.tagName === e.CAROUSEL)
237
- return t.children.some((s) => s.tagName !== e.BUBBLE) && h("Carousel can only have Bubble as children"), t.children.length > 12 && h("Carousel can have maximum 12 bubbles"), c.carousel(
238
- t.children.map(m)
251
+ return t.children.some((c) => c.tagName !== e.BUBBLE) && h("Carousel can only have Bubble as children"), t.children.length > 12 && h("Carousel can have maximum 12 bubbles"), o.carousel(
252
+ t.children.map(u)
239
253
  );
240
- if (J.includes(t.tagName))
241
- return t.children.length !== 1 && h(`${t.tagName} should have exactly one child node`), m(t.children[0]);
254
+ if (Q.includes(t.tagName))
255
+ return t.children.length !== 1 && h(`${t.tagName} should have exactly one child node`), u(t.children[0]);
242
256
  if (t.tagName === e.BUTTON)
243
- return t.attributes.action || h("Button component should contain an action node"), c.button(t.attributes);
257
+ return t.attributes.action || h("Button component should contain an action node"), o.button(t.attributes);
244
258
  if (t.tagName === e.SPAN) {
245
259
  t.children.length > 1 && h("Span component can only have one text child");
246
- const s = t.children[0];
247
- if (s && s.type === E.TEXT)
248
- return c.span(
249
- s.content,
260
+ const c = t.children[0];
261
+ if (c && c.type === E.TEXT)
262
+ return o.span(
263
+ c.content,
250
264
  t.attributes
251
265
  );
252
- h(s ? "Span component only allowed text child" : "Span component can not be empty");
266
+ h(c ? "Span component only allowed text child" : "Span component can not be empty");
253
267
  }
254
268
  if (t.tagName === e.TEXT)
255
- return c.text(
256
- t.children.map(m),
269
+ return o.text(
270
+ t.children.map(u),
257
271
  t.attributes
258
272
  );
259
273
  if (t.tagName === e.SEPARATOR)
260
- return c.separator(t.attributes);
274
+ return o.separator(t.attributes);
261
275
  if (t.tagName === e.IMAGE)
262
- return c.image(t.attributes);
276
+ return o.image(t.attributes);
263
277
  if (t.tagName === e.VIDEO)
264
- return t.children.length !== 1 && h("Video component should only has one Box or Image as altContent"), t.attributes.altContent = m(t.children[0]), c.video(t.attributes);
278
+ return t.children.length !== 1 && h("Video component should only has one Box or Image as altContent"), t.attributes.altContent = u(t.children[0]), o.video(t.attributes);
265
279
  if (t.tagName === e.ICON)
266
- return c.icon(t.attributes);
280
+ return o.icon(t.attributes);
267
281
  if (t.tagName === e.FILLER)
268
- return c.filler(t.attributes);
282
+ return o.filler(t.attributes);
269
283
  if (t.tagName === e.BOX || t.tagName === e.DIV || t.tagName === e.ARTICLE)
270
- return t.attributes.layout || (t.attributes.layout = "vertical"), c.box(
271
- t.children.map(m),
284
+ return t.attributes.layout || (t.attributes.layout = "vertical"), o.box(
285
+ t.children.map(u),
272
286
  t.attributes
273
287
  );
274
288
  if (t.tagName === e.SPACE)
275
- return c.span(" ".repeat(t.attributes.size || 1));
289
+ return o.span(" ".repeat(t.attributes.size || 1));
276
290
  if (t.tagName === e.STRONG)
277
- return t.tagName = e.TEXT, t.attributes.weight = "bold", m(t);
291
+ return t.tagName = e.TEXT, t.attributes.weight = "bold", u(t);
278
292
  if (t.tagName === e.BASELINE)
279
- return t.tagName = e.BOX, t.attributes.layout = "baseline", m(t);
293
+ return t.tagName = e.BOX, t.attributes.layout = "baseline", u(t);
280
294
  if (t.tagName === e.ROW)
281
- return t.tagName = e.BOX, t.attributes.layout = "horizontal", m(t);
295
+ return t.tagName = e.BOX, t.attributes.layout = "horizontal", u(t);
282
296
  if (t.tagName === e.VERTICAL)
283
- return t.tagName = e.BOX, t.attributes.layout = "vertical", m(t);
297
+ return t.tagName = e.BOX, t.attributes.layout = "vertical", u(t);
284
298
  if (t.tagName === e.ACTION) {
285
- const s = t.attributes.type;
286
- switch (s) {
299
+ const c = t.attributes.type;
300
+ switch (c) {
287
301
  case "uri":
288
- return c.uri(t.attributes);
302
+ return o.uri(t.attributes);
289
303
  case "postback":
290
- return c.postback(t.attributes);
304
+ return o.postback(t.attributes);
291
305
  case "message":
292
- return c.message(t.attributes);
306
+ return o.message(t.attributes);
293
307
  case "datetimepicker":
294
- return c.datetimepicker(t.attributes);
308
+ return o.datetimepicker(t.attributes);
295
309
  case "camera":
296
- return c.camera(t.attributes);
310
+ return o.camera(t.attributes);
297
311
  case "cameraRoll":
298
- return c.cameraRoll(t.attributes);
312
+ return o.cameraRoll(t.attributes);
299
313
  case "location":
300
- return c.location(t.attributes);
314
+ return o.location(t.attributes);
301
315
  case "richmenuswitch":
302
- return c.richmenuswitch(t.attributes);
316
+ return o.richmenuswitch(t.attributes);
303
317
  case "clipboard":
304
- return c.clipboard(t.attributes);
318
+ return o.clipboard(t.attributes);
305
319
  default:
306
- h(`Unsupported action type: ${s}`);
320
+ h(`Unsupported action type: ${c}`);
307
321
  }
308
322
  }
309
323
  if (t.tagName === e.BACKGROUND)
310
324
  return t.attributes;
311
325
  }
312
- const o = {
313
- createElement: (t, r = {}, n = [], s = 0) => {
314
- const i = Object.entries(r).filter(([u, p]) => p != null).map(([u, p]) => typeof p == "object" ? `${u}="${JSON.stringify(p).replace(/"/g, "&quot;")}"` : `${u}="${p}"`).join(" "), a = i ? ` ${i}` : "";
315
- if (n.length === 0)
316
- return ` <${t}${a} />`;
326
+ const m = {
327
+ createElement: (t, r = {}, a = [], c = 0) => {
328
+ const i = Object.entries(r).filter(([p, l]) => l != null).map(([p, l]) => typeof l == "object" ? `${p}="${JSON.stringify(l).replace(/"/g, "&quot;")}"` : `${p}="${l}"`).join(" "), n = i ? ` ${i}` : "";
329
+ if (a.length === 0)
330
+ return ` <${t}${n} />`;
317
331
  {
318
- const u = n.map((p) => typeof p == "string" ? p.split(`
319
- `).map((f) => f ? " " + f : "").join(`
320
- `) : p).join(`
332
+ const p = a.map((l) => typeof l == "string" ? l.split(`
333
+ `).map((b) => b ? " " + b : "").join(`
334
+ `) : l).join(`
321
335
  `);
322
- return ` <${t}${a}>
323
- ${u}
336
+ return ` <${t}${n}>
337
+ ${p}
324
338
  </${t}>`;
325
339
  }
326
340
  },
327
341
  createText: (t) => t
328
- };
329
- function b(t, r = 0) {
342
+ }, A = (t, r, a, c) => {
343
+ const s = [f(r, c + 1)];
344
+ return a && Object.keys(a).length > 0 && s.unshift(f(D("style", a), c + 1)), m.createElement(t, {}, s, c);
345
+ }, D = (t, r) => ({ ...r, type: t });
346
+ function f(t, r = 0) {
330
347
  if (!t || typeof t != "object")
331
348
  return "";
332
- const { type: n, ...s } = t, { action: l, ...i } = s, a = l ? b(l, r + 1) : "";
333
- switch (n) {
349
+ const { type: a, ...c } = t, { action: s, ...i } = c, n = s ? f(s, r + 1) : "";
350
+ switch (a) {
334
351
  case "carousel":
335
- const { contents: u, ...p } = i, f = u.map((N) => b(N, r + 1));
336
- return a && f.unshift(a), o.createElement(e.CAROUSEL, p, f, r);
352
+ const { contents: p, ...l } = i, b = p.map((N) => f(N, r + 1));
353
+ return n && b.unshift(n), m.createElement(e.CAROUSEL, l, b, r);
337
354
  case "bubble":
338
- const { header: y, hero: C, body: R, footer: B, ...L } = i, g = [];
339
- return a && g.push(a), y && g.push(o.createElement(e.HEADER, {}, [b(y, r + 1)], r + 1)), C && g.push(o.createElement(e.HERO, {}, [b(C, r + 1)], r + 1)), R && g.push(o.createElement(e.BODY, {}, [b(R, r + 1)], r + 1)), B && g.push(o.createElement(e.FOOTER, {}, [b(B, r + 1)], r + 1)), o.createElement(e.BUBBLE, L, g, r);
355
+ const { header: R, hero: B, body: x, footer: S, styles: T = {}, ...k } = i, g = [];
356
+ return n && g.push(n), R && g.push(A(e.HEADER, R, T.header, r + 1)), B && g.push(A(e.HERO, B, T.hero, r + 1)), x && g.push(A(e.BODY, x, T.body, r + 1)), S && g.push(A(e.FOOTER, S, T.footer, r + 1)), m.createElement(e.BUBBLE, k, g, r);
340
357
  case "box":
341
- const { contents: w, background: d, ...O } = i;
342
- O.layout || (O.layout = "vertical");
343
- const T = [];
344
- return a && T.push(a), d && T.push(b({ type: "background", ...d }, r + 1)), T.push(...(w || []).map((N) => b(N, r + 1))), o.createElement(e.BOX, O, T, r);
358
+ const { contents: X, background: I, ...C } = i;
359
+ C.layout || (C.layout = "vertical");
360
+ const y = [];
361
+ return n && y.push(n), I && y.push(f(D("background", I), r + 1)), y.push(...(X || []).map((N) => f(N, r + 1))), m.createElement(e.BOX, C, y, r);
345
362
  case "text":
346
- const { text: U, contents: A, ...k } = i, x = A && A.length > 0 ? A.map((N) => b(N, r + 1)) : [U || ""];
347
- return a && x.unshift(a), o.createElement(e.TEXT, k, x, r);
363
+ const { text: $, contents: d, ...G } = i, L = d && d.length > 0 ? d.map((N) => f(N, r + 1)) : [$ || ""];
364
+ return n && L.unshift(n), m.createElement(e.TEXT, G, L, r);
348
365
  case "span":
349
- const { text: D, ...X } = i, I = [D || ""];
350
- return a && I.unshift(a), o.createElement(e.SPAN, X, I, r);
366
+ const { text: H, ...F } = i, v = [H || ""];
367
+ return n && v.unshift(n), m.createElement(e.SPAN, F, v, r);
351
368
  case "image":
352
- const { ...$ } = i, G = a ? [a] : [];
353
- return o.createElement(e.IMAGE, $, G, r);
369
+ const { ...V } = i, M = n ? [n] : [];
370
+ return m.createElement(e.IMAGE, V, M, r);
354
371
  case "video":
355
- const { altContent: S, ...H } = i, v = S ? [b(S, r + 1)] : [];
356
- return a && v.unshift(a), o.createElement(e.VIDEO, H, v, r);
372
+ const { altContent: w, ...P } = i, U = w ? [f(w, r + 1)] : [];
373
+ return n && U.unshift(n), m.createElement(e.VIDEO, P, U, r);
357
374
  case "icon":
358
- const { ...F } = i, V = a ? [a] : [];
359
- return o.createElement(e.ICON, F, V, r);
375
+ const { ...j } = i, Y = n ? [n] : [];
376
+ return m.createElement(e.ICON, j, Y, r);
360
377
  case "button":
361
- const { ...M } = i, P = a ? [a] : [];
362
- return o.createElement(e.BUTTON, M, P, r);
378
+ const { ...K } = i, W = n ? [n] : [];
379
+ return m.createElement(e.BUTTON, K, W, r);
363
380
  case "separator":
364
- const K = a ? [a] : [];
365
- return o.createElement(e.SEPARATOR, i, K, r);
381
+ const J = n ? [n] : [];
382
+ return m.createElement(e.SEPARATOR, i, J, r);
366
383
  case "filler":
367
- const j = a ? [a] : [];
368
- return o.createElement(e.FILLER, i, j, r);
384
+ const z = n ? [n] : [];
385
+ return m.createElement(e.FILLER, i, z, r);
369
386
  // Action types
370
387
  case "uri":
371
388
  case "message":
@@ -376,21 +393,23 @@ function b(t, r = 0) {
376
393
  case "location":
377
394
  case "richmenuswitch":
378
395
  case "clipboard":
379
- return o.createElement(e.ACTION, { type: n, ...i }, [], r);
396
+ return m.createElement(e.ACTION, { type: a, ...i }, [], r);
380
397
  case "background":
381
- return o.createElement(e.BACKGROUND, i, [], r);
398
+ return m.createElement(e.BACKGROUND, i, [], r);
399
+ case "style":
400
+ return m.createElement(e.STYLE, i, [], r);
382
401
  default:
383
402
  return "";
384
403
  }
385
404
  }
386
- function nt(t) {
405
+ function lt(t) {
387
406
  try {
388
- return _(t).map(m);
407
+ return at(t).map(u);
389
408
  } catch (r) {
390
409
  throw console.error("Error converting HTML to Flex Message:", r), r;
391
410
  }
392
411
  }
393
412
  export {
394
- b as convertJsonToHtml,
395
- nt as default
413
+ f as convertJsonToHtml,
414
+ lt as default
396
415
  };
@@ -1,6 +1,6 @@
1
- (function(b,T){typeof exports=="object"&&typeof module<"u"?T(exports,require("htmlparser2")):typeof define=="function"&&define.amd?define(["exports","htmlparser2"],T):(b=typeof globalThis<"u"?globalThis:b||self,T(b.FlexHtmlRender={},b.htmlparser2))})(this,(function(b,T){"use strict";const E={ELEMENT:"element",TEXT:"text"},e={CAROUSEL:"carousel",BUBBLE:"bubble",HEADER:"header",HERO:"hero",BODY:"body",FOOTER:"footer",BOX:"box",TEXT:"text",SPAN:"span",IMAGE:"image",VIDEO:"video",ICON:"icon",SEPARATOR:"separator",BUTTON:"button",FILLER:"filler",SPACE:"space",STRONG:"strong",BASELINE:"baseline",ROW:"row",VERTICAL:"vertical",DIV:"div",ARTICLE:"article",ACTION:"action",BACKGROUND:"background"},U=[e.HEADER,e.HERO,e.BODY,e.FOOTER],k=[e.BOX,e.VERTICAL,e.ROW,e.BASELINE,e.DIV,e.ARTICLE],D=[e.TEXT,e.STRONG];function X(t){const r=Number(t);return isNaN(r)?t:r}function $(t){return t==="true"?!0:t==="false"?!1:t}function H(t){return[X,$].reduce((n,s)=>s(n),t)}function G(t){const r={type:"root",children:[]},n=[r],s=new T.Parser({onopentag(l,i){const a={type:E.ELEMENT,tagName:l,attributes:Object.fromEntries(Object.entries(i).map(([h,g])=>[h,H(g)])),children:[]};n[n.length-1].children.push(a),n.push(a)},ontext(l){const i=n[n.length-1];if(i.tagName==="span"){i.children.push({type:E.TEXT,content:l});return}l.trim()&&i.children.push({type:E.TEXT,content:l.replace(/\n/g,"").trim()})},onclosetag(){n.pop()},onerror(l){console.error("解析錯誤:",l)}},{xmlMode:!0,lowerCaseTags:!1,lowerCaseAttributeNames:!1});return s.write(t),s.end(),r.children}const p=t=>{throw Error(`[Flex Html Render]: ${t}`)},c={carousel:t=>({type:"carousel",contents:t}),bubble:(t={})=>({type:"bubble",...t}),box:(t,r={})=>({type:"box",contents:t,...r}),text:(t,r={})=>({type:"text",...typeof t=="string"?{text:t}:{contents:t},...r}),span:(t,r={})=>({type:"span",text:t,...r}),image:(t={})=>({type:"image",...t}),video:(t={})=>({type:"video",...t}),icon:(t={})=>({type:"icon",...t}),button:(t={})=>({type:"button",...t}),separator:(t={color:"#E0E3EA"})=>({type:"separator",...t}),filler:(t={})=>({type:"filler",...t}),uri:(t={})=>({type:"uri",altUri:t.altUri?{desktop:t.altUri}:void 0,...t}),postback:(t={})=>({type:"postback",...t}),message:(t={})=>({type:"message",...t}),datetimepicker:(t={})=>({type:"datetimepicker",...t}),camera:(t={})=>({type:"camera",...t}),cameraRoll:(t={})=>({type:"cameraRoll",...t}),location:(t={})=>({type:"location",...t}),richmenuswitch:(t={})=>({type:"richmenuswitch",...t}),clipboard:(t={})=>({type:"clipboard",...t})};function F(t){if(t.tagName!==e.BACKGROUND&&t.children&&t.children.length>0){const r=t.children.filter(n=>n.tagName===e.BACKGROUND);return r.length>1&&p("A box node can only allowed with one background node inside"),t.children=t.children.filter(n=>n.tagName!==e.BACKGROUND),r[0]}return null}function M(t){if(t.tagName!==e.ACTION&&t.children&&t.children.length>0){const r=t.children.filter(n=>n.tagName===e.ACTION);return r.length>1&&p("A node can only allowed with one action node inside"),t.children=t.children.filter(n=>n.tagName!==e.ACTION),r[0]}return null}function P(t){![...D,e.SPAN].includes(t.tagName)&&t.children?.some(l=>l.type===E.TEXT)&&(console.log(t),p("Span component only allowed inside Text component")),t.tagName===e.VIDEO&&t.children?.some(l=>![e.BOX,e.IMAGE].includes(l.tagName))&&p("Video component only allowed Box or Image as altContent child"),!k.includes(t.tagName)&&t.children?.some(l=>l.tagName===e.BACKGROUND)&&p("Only Box component allowed to have Background child")}function u(t){t.attributes=t.attributes||{},P(t);const r=M(t);r&&(t.attributes.action=u(r));const n=F(t);if(n&&(t.attributes.background=u(n)),t.type===E.TEXT)return c.span(t.content,t.attributes);if(t.tagName===e.BUBBLE){const s=t.children.find(m=>m.tagName===e.HEADER),l=t.children.find(m=>m.tagName===e.HERO),i=t.children.find(m=>m.tagName===e.BODY),a=t.children.find(m=>m.tagName===e.FOOTER);return c.bubble({...t.attributes,header:s?u(s):void 0,hero:l?u(l):void 0,body:i?u(i):void 0,footer:a?u(a):void 0})}if(t.tagName===e.CAROUSEL)return t.children.some(s=>s.tagName!==e.BUBBLE)&&p("Carousel can only have Bubble as children"),t.children.length>12&&p("Carousel can have maximum 12 bubbles"),c.carousel(t.children.map(u));if(U.includes(t.tagName))return t.children.length!==1&&p(`${t.tagName} should have exactly one child node`),u(t.children[0]);if(t.tagName===e.BUTTON)return t.attributes.action||p("Button component should contain an action node"),c.button(t.attributes);if(t.tagName===e.SPAN){t.children.length>1&&p("Span component can only have one text child");const s=t.children[0];if(s&&s.type===E.TEXT)return c.span(s.content,t.attributes);p(s?"Span component only allowed text child":"Span component can not be empty")}if(t.tagName===e.TEXT)return c.text(t.children.map(u),t.attributes);if(t.tagName===e.SEPARATOR)return c.separator(t.attributes);if(t.tagName===e.IMAGE)return c.image(t.attributes);if(t.tagName===e.VIDEO)return t.children.length!==1&&p("Video component should only has one Box or Image as altContent"),t.attributes.altContent=u(t.children[0]),c.video(t.attributes);if(t.tagName===e.ICON)return c.icon(t.attributes);if(t.tagName===e.FILLER)return c.filler(t.attributes);if(t.tagName===e.BOX||t.tagName===e.DIV||t.tagName===e.ARTICLE)return t.attributes.layout||(t.attributes.layout="vertical"),c.box(t.children.map(u),t.attributes);if(t.tagName===e.SPACE)return c.span(" ".repeat(t.attributes.size||1));if(t.tagName===e.STRONG)return t.tagName=e.TEXT,t.attributes.weight="bold",u(t);if(t.tagName===e.BASELINE)return t.tagName=e.BOX,t.attributes.layout="baseline",u(t);if(t.tagName===e.ROW)return t.tagName=e.BOX,t.attributes.layout="horizontal",u(t);if(t.tagName===e.VERTICAL)return t.tagName=e.BOX,t.attributes.layout="vertical",u(t);if(t.tagName===e.ACTION){const s=t.attributes.type;switch(s){case"uri":return c.uri(t.attributes);case"postback":return c.postback(t.attributes);case"message":return c.message(t.attributes);case"datetimepicker":return c.datetimepicker(t.attributes);case"camera":return c.camera(t.attributes);case"cameraRoll":return c.cameraRoll(t.attributes);case"location":return c.location(t.attributes);case"richmenuswitch":return c.richmenuswitch(t.attributes);case"clipboard":return c.clipboard(t.attributes);default:p(`Unsupported action type: ${s}`)}}if(t.tagName===e.BACKGROUND)return t.attributes}const o={createElement:(t,r={},n=[],s=0)=>{const i=Object.entries(r).filter(([m,h])=>h!=null).map(([m,h])=>typeof h=="object"?`${m}="${JSON.stringify(h).replace(/"/g,"&quot;")}"`:`${m}="${h}"`).join(" "),a=i?` ${i}`:"";if(n.length===0)return` <${t}${a} />`;{const m=n.map(h=>typeof h=="string"?h.split(`
1
+ (function(b,T){typeof exports=="object"&&typeof module<"u"?T(exports,require("htmlparser2")):typeof define=="function"&&define.amd?define(["exports","htmlparser2"],T):(b=typeof globalThis<"u"?globalThis:b||self,T(b.FlexHtmlRender={},b.htmlparser2))})(this,(function(b,T){"use strict";const E={ELEMENT:"element",TEXT:"text"},e={CAROUSEL:"carousel",BUBBLE:"bubble",HEADER:"header",HERO:"hero",BODY:"body",FOOTER:"footer",BOX:"box",TEXT:"text",SPAN:"span",IMAGE:"image",VIDEO:"video",ICON:"icon",SEPARATOR:"separator",BUTTON:"button",FILLER:"filler",SPACE:"space",STRONG:"strong",BASELINE:"baseline",ROW:"row",VERTICAL:"vertical",DIV:"div",ARTICLE:"article",ACTION:"action",BACKGROUND:"background",STYLE:"style"},$=[e.HEADER,e.HERO,e.BODY,e.FOOTER],H=[e.BOX,e.VERTICAL,e.ROW,e.BASELINE,e.DIV,e.ARTICLE],G=[e.TEXT,e.STRONG];function F(t){const r=Number(t);return isNaN(r)?t:r}function M(t){return t==="true"?!0:t==="false"?!1:t}function P(t){return[F,M].reduce((a,c)=>c(a),t)}function V(t){const r={type:"root",children:[]},a=[r],c=new T.Parser({onopentag(s,i){const n={type:E.ELEMENT,tagName:s,attributes:Object.fromEntries(Object.entries(i).map(([l,g])=>[l,P(g)])),children:[]};a[a.length-1].children.push(n),a.push(n)},ontext(s){const i=a[a.length-1];if(i.tagName==="span"){i.children.push({type:E.TEXT,content:s});return}s.trim()&&i.children.push({type:E.TEXT,content:s.replace(/\n/g,"").trim()})},onclosetag(){a.pop()},onerror(s){console.error("解析錯誤:",s)}},{xmlMode:!0,lowerCaseTags:!1,lowerCaseAttributeNames:!1});return c.write(t),c.end(),r.children}const h=t=>{throw Error(`[Flex Html Render]: ${t}`)},o={carousel:t=>({type:"carousel",contents:t}),bubble:(t={})=>({type:"bubble",...t}),box:(t,r={})=>({type:"box",contents:t,...r}),text:(t,r={})=>({type:"text",...typeof t=="string"?{text:t}:{contents:t},...r}),span:(t,r={})=>({type:"span",text:t,...r}),image:(t={})=>({type:"image",...t}),video:(t={})=>({type:"video",...t}),icon:(t={})=>({type:"icon",...t}),button:(t={})=>({type:"button",...t}),separator:(t={color:"#E0E3EA"})=>({type:"separator",...t}),filler:(t={})=>({type:"filler",...t}),uri:(t={})=>({type:"uri",altUri:t.altUri?{desktop:t.altUri}:void 0,...t}),postback:(t={})=>({type:"postback",...t}),message:(t={})=>({type:"message",...t}),datetimepicker:(t={})=>({type:"datetimepicker",...t}),camera:(t={})=>({type:"camera",...t}),cameraRoll:(t={})=>({type:"cameraRoll",...t}),location:(t={})=>({type:"location",...t}),richmenuswitch:(t={})=>({type:"richmenuswitch",...t}),clipboard:(t={})=>({type:"clipboard",...t})};function j(t){if(t.tagName!==e.BACKGROUND&&t.children&&t.children.length>0){const r=t.children.filter(a=>a.tagName===e.BACKGROUND);return r.length>1&&h("A box node can only allowed with one background node inside"),t.children=t.children.filter(a=>a.tagName!==e.BACKGROUND),r[0]}return null}function Y(t){if(t.tagName!==e.ACTION&&t.children&&t.children.length>0){const r=t.children.filter(a=>a.tagName===e.ACTION);return r.length>1&&h("A node can only allowed with one action node inside"),t.children=t.children.filter(a=>a.tagName!==e.ACTION),r[0]}return null}function K(t){![...G,e.SPAN].includes(t.tagName)&&t.children?.some(s=>s.type===E.TEXT)&&h("Span component only allowed inside Text component"),t.tagName===e.VIDEO&&t.children?.some(s=>![e.BOX,e.IMAGE].includes(s.tagName))&&h("Video component only allowed Box or Image as altContent child"),!H.includes(t.tagName)&&t.children?.some(s=>s.tagName===e.BACKGROUND)&&h("Only Box component allowed to have Background child")}function d(t){if(t){const r=t.children.find(a=>a.tagName===e.STYLE);if(r)return t.children=t.children.filter(a=>a.tagName!==e.STYLE),r.attributes}}function u(t){t.attributes=t.attributes||{},K(t);const r=Y(t);r&&(t.attributes.action=u(r));const a=j(t);if(a&&(t.attributes.background=u(a)),t.type===E.TEXT)return o.span(t.content,t.attributes);if(t.tagName===e.BUBBLE){const c=t.children.find(l=>l.tagName===e.HEADER),s=t.children.find(l=>l.tagName===e.HERO),i=t.children.find(l=>l.tagName===e.BODY),n=t.children.find(l=>l.tagName===e.FOOTER),p={header:d(c),hero:d(s),body:d(i),footer:d(n)};return o.bubble({...t.attributes,header:c?u(c):void 0,hero:s?u(s):void 0,body:i?u(i):void 0,footer:n?u(n):void 0,styles:Object.values(p).some(l=>l!==void 0)?p:void 0})}if(t.tagName===e.CAROUSEL)return t.children.some(c=>c.tagName!==e.BUBBLE)&&h("Carousel can only have Bubble as children"),t.children.length>12&&h("Carousel can have maximum 12 bubbles"),o.carousel(t.children.map(u));if($.includes(t.tagName))return t.children.length!==1&&h(`${t.tagName} should have exactly one child node`),u(t.children[0]);if(t.tagName===e.BUTTON)return t.attributes.action||h("Button component should contain an action node"),o.button(t.attributes);if(t.tagName===e.SPAN){t.children.length>1&&h("Span component can only have one text child");const c=t.children[0];if(c&&c.type===E.TEXT)return o.span(c.content,t.attributes);h(c?"Span component only allowed text child":"Span component can not be empty")}if(t.tagName===e.TEXT)return o.text(t.children.map(u),t.attributes);if(t.tagName===e.SEPARATOR)return o.separator(t.attributes);if(t.tagName===e.IMAGE)return o.image(t.attributes);if(t.tagName===e.VIDEO)return t.children.length!==1&&h("Video component should only has one Box or Image as altContent"),t.attributes.altContent=u(t.children[0]),o.video(t.attributes);if(t.tagName===e.ICON)return o.icon(t.attributes);if(t.tagName===e.FILLER)return o.filler(t.attributes);if(t.tagName===e.BOX||t.tagName===e.DIV||t.tagName===e.ARTICLE)return t.attributes.layout||(t.attributes.layout="vertical"),o.box(t.children.map(u),t.attributes);if(t.tagName===e.SPACE)return o.span(" ".repeat(t.attributes.size||1));if(t.tagName===e.STRONG)return t.tagName=e.TEXT,t.attributes.weight="bold",u(t);if(t.tagName===e.BASELINE)return t.tagName=e.BOX,t.attributes.layout="baseline",u(t);if(t.tagName===e.ROW)return t.tagName=e.BOX,t.attributes.layout="horizontal",u(t);if(t.tagName===e.VERTICAL)return t.tagName=e.BOX,t.attributes.layout="vertical",u(t);if(t.tagName===e.ACTION){const c=t.attributes.type;switch(c){case"uri":return o.uri(t.attributes);case"postback":return o.postback(t.attributes);case"message":return o.message(t.attributes);case"datetimepicker":return o.datetimepicker(t.attributes);case"camera":return o.camera(t.attributes);case"cameraRoll":return o.cameraRoll(t.attributes);case"location":return o.location(t.attributes);case"richmenuswitch":return o.richmenuswitch(t.attributes);case"clipboard":return o.clipboard(t.attributes);default:h(`Unsupported action type: ${c}`)}}if(t.tagName===e.BACKGROUND)return t.attributes}const m={createElement:(t,r={},a=[],c=0)=>{const i=Object.entries(r).filter(([p,l])=>l!=null).map(([p,l])=>typeof l=="object"?`${p}="${JSON.stringify(l).replace(/"/g,"&quot;")}"`:`${p}="${l}"`).join(" "),n=i?` ${i}`:"";if(a.length===0)return` <${t}${n} />`;{const p=a.map(l=>typeof l=="string"?l.split(`
2
2
  `).map(g=>g?" "+g:"").join(`
3
- `):h).join(`
4
- `);return` <${t}${a}>
5
- ${m}
6
- </${t}>`}},createText:t=>t};function f(t,r=0){if(!t||typeof t!="object")return"";const{type:n,...s}=t,{action:l,...i}=s,a=l?f(l,r+1):"";switch(n){case"carousel":const{contents:m,...h}=i,g=m.map(O=>f(O,r+1));return a&&g.unshift(a),o.createElement(e.CAROUSEL,h,g,r);case"bubble":const{header:d,hero:R,body:B,footer:x,...j}=i,N=[];return a&&N.push(a),d&&N.push(o.createElement(e.HEADER,{},[f(d,r+1)],r+1)),R&&N.push(o.createElement(e.HERO,{},[f(R,r+1)],r+1)),B&&N.push(o.createElement(e.BODY,{},[f(B,r+1)],r+1)),x&&N.push(o.createElement(e.FOOTER,{},[f(x,r+1)],r+1)),o.createElement(e.BUBBLE,j,N,r);case"box":const{contents:K,background:S,...A}=i;A.layout||(A.layout="vertical");const y=[];return a&&y.push(a),S&&y.push(f({type:"background",...S},r+1)),y.push(...(K||[]).map(O=>f(O,r+1))),o.createElement(e.BOX,A,y,r);case"text":const{text:J,contents:C,...Y}=i,I=C&&C.length>0?C.map(O=>f(O,r+1)):[J||""];return a&&I.unshift(a),o.createElement(e.TEXT,Y,I,r);case"span":const{text:W,...q}=i,v=[W||""];return a&&v.unshift(a),o.createElement(e.SPAN,q,v,r);case"image":const{...z}=i,_=a?[a]:[];return o.createElement(e.IMAGE,z,_,r);case"video":const{altContent:L,...Q}=i,w=L?[f(L,r+1)]:[];return a&&w.unshift(a),o.createElement(e.VIDEO,Q,w,r);case"icon":const{...Z}=i,tt=a?[a]:[];return o.createElement(e.ICON,Z,tt,r);case"button":const{...et}=i,rt=a?[a]:[];return o.createElement(e.BUTTON,et,rt,r);case"separator":const at=a?[a]:[];return o.createElement(e.SEPARATOR,i,at,r);case"filler":const nt=a?[a]:[];return o.createElement(e.FILLER,i,nt,r);case"uri":case"message":case"postback":case"datetimepicker":case"camera":case"cameraRoll":case"location":case"richmenuswitch":case"clipboard":return o.createElement(e.ACTION,{type:n,...i},[],r);case"background":return o.createElement(e.BACKGROUND,i,[],r);default:return""}}function V(t){try{return G(t).map(u)}catch(r){throw console.error("Error converting HTML to Flex Message:",r),r}}b.convertJsonToHtml=f,b.default=V,Object.defineProperties(b,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
3
+ `):l).join(`
4
+ `);return` <${t}${n}>
5
+ ${p}
6
+ </${t}>`}},createText:t=>t},O=(t,r,a,c)=>{const s=[f(r,c+1)];return a&&Object.keys(a).length>0&&s.unshift(f(x("style",a),c+1)),m.createElement(t,{},s,c)},x=(t,r)=>({...r,type:t});function f(t,r=0){if(!t||typeof t!="object")return"";const{type:a,...c}=t,{action:s,...i}=c,n=s?f(s,r+1):"";switch(a){case"carousel":const{contents:p,...l}=i,g=p.map(y=>f(y,r+1));return n&&g.unshift(n),m.createElement(e.CAROUSEL,l,g,r);case"bubble":const{header:S,hero:I,body:v,footer:L,styles:A={},...W}=i,N=[];return n&&N.push(n),S&&N.push(O(e.HEADER,S,A.header,r+1)),I&&N.push(O(e.HERO,I,A.hero,r+1)),v&&N.push(O(e.BODY,v,A.body,r+1)),L&&N.push(O(e.FOOTER,L,A.footer,r+1)),m.createElement(e.BUBBLE,W,N,r);case"box":const{contents:q,background:w,...R}=i;R.layout||(R.layout="vertical");const C=[];return n&&C.push(n),w&&C.push(f(x("background",w),r+1)),C.push(...(q||[]).map(y=>f(y,r+1))),m.createElement(e.BOX,R,C,r);case"text":const{text:z,contents:B,..._}=i,U=B&&B.length>0?B.map(y=>f(y,r+1)):[z||""];return n&&U.unshift(n),m.createElement(e.TEXT,_,U,r);case"span":const{text:Q,...Z}=i,D=[Q||""];return n&&D.unshift(n),m.createElement(e.SPAN,Z,D,r);case"image":const{...tt}=i,et=n?[n]:[];return m.createElement(e.IMAGE,tt,et,r);case"video":const{altContent:k,...rt}=i,X=k?[f(k,r+1)]:[];return n&&X.unshift(n),m.createElement(e.VIDEO,rt,X,r);case"icon":const{...at}=i,nt=n?[n]:[];return m.createElement(e.ICON,at,nt,r);case"button":const{...it}=i,ct=n?[n]:[];return m.createElement(e.BUTTON,it,ct,r);case"separator":const st=n?[n]:[];return m.createElement(e.SEPARATOR,i,st,r);case"filler":const lt=n?[n]:[];return m.createElement(e.FILLER,i,lt,r);case"uri":case"message":case"postback":case"datetimepicker":case"camera":case"cameraRoll":case"location":case"richmenuswitch":case"clipboard":return m.createElement(e.ACTION,{type:a,...i},[],r);case"background":return m.createElement(e.BACKGROUND,i,[],r);case"style":return m.createElement(e.STYLE,i,[],r);default:return""}}function J(t){try{return V(t).map(u)}catch(r){throw console.error("Error converting HTML to Flex Message:",r),r}}b.convertJsonToHtml=f,b.default=J,Object.defineProperties(b,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flex-html-render",
3
- "version": "1.0.3",
3
+ "version": "1.1.1",
4
4
  "description": "將 HTML 字串轉換為 LINE Flex Message JSON 結構的工具",
5
5
  "type": "module",
6
6
  "main": "dist/flex-html-render.cjs.js",