cardbuddy 0.22.0 → 0.24.0

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.
Files changed (76) hide show
  1. package/README.md +11 -11
  2. package/dist/cli.js +2 -2
  3. package/dist/config.js +3 -3
  4. package/dist/init.js +13 -13
  5. package/dist/mcp.js +1 -1
  6. package/dist/preview-editor/assets/{index-CPpLZZyi.js → index-BumNBo-n.js} +81 -80
  7. package/dist/preview-editor/assets/{index-CU8zVA2P.css → index-MWKPP9nd.css} +1 -1
  8. package/dist/preview-editor/index.html +2 -2
  9. package/dist/skills-upgrade.js +1 -1
  10. package/dist/standalone.js +2 -2
  11. package/dist/vendored/card-editor/runtime/adapter.js +10 -11
  12. package/dist/vendored/mcp/client.mjs +50 -13
  13. package/dist/vendored/mcp/tools.mjs +13 -7
  14. package/dist/vendored/server/core/ai-chat.js +540 -183
  15. package/dist/vendored/server/core/assetProxy.js +1 -1
  16. package/dist/vendored/server/core/browser.js +1 -1
  17. package/dist/vendored/server/core/cardCompile.js +202 -0
  18. package/dist/vendored/server/core/cardDigest.js +324 -0
  19. package/dist/vendored/server/core/cardExport.js +5 -5
  20. package/dist/vendored/server/core/cardFiles.js +1 -1
  21. package/dist/vendored/server/core/cardHistoryRemote.js +1 -1
  22. package/dist/vendored/server/core/cardImportMeta.js +1 -1
  23. package/dist/vendored/server/core/cardListCache.js +92 -10
  24. package/dist/vendored/server/core/cardPublish.js +11 -6
  25. package/dist/vendored/server/core/cardPublishVersions.js +8 -8
  26. package/dist/vendored/server/core/cardSave.js +14 -10
  27. package/dist/vendored/server/core/cardSidecar.js +3 -3
  28. package/dist/vendored/server/core/cardSync.js +35 -23
  29. package/dist/vendored/server/core/cardVersion.js +1 -1
  30. package/dist/vendored/server/core/flameCompile/client.js +466 -0
  31. package/dist/vendored/server/core/flameCompile/clientElements.js +372 -0
  32. package/dist/vendored/server/core/flameCompile/common.js +253 -0
  33. package/dist/vendored/server/core/flameCompile/h5.js +326 -0
  34. package/dist/vendored/server/core/flameCompile/index.js +126 -0
  35. package/dist/vendored/server/core/flameCompile/model.js +162 -0
  36. package/dist/vendored/server/core/index.js +12 -6
  37. package/dist/vendored/server/core/logger.js +1 -1
  38. package/dist/vendored/server/core/remoteCard.js +5 -5
  39. package/dist/vendored/server/core/skillPaths.js +1 -1
  40. package/dist/vendored/server/core/sliceUpload.js +4 -4
  41. package/dist/vendored/skills/README.md +13 -13
  42. package/dist/vendored/skills/card-doctor/SKILL.md +2 -2
  43. package/dist/vendored/skills/dsl-card-ai/SKILL.md +4 -4
  44. package/dist/vendored/skills/dsl-card-generator/SKILL.md +2 -2
  45. package/dist/vendored/skills/dsl-card-generator/references/amount_formatting.md +1 -1
  46. package/dist/vendored/skills/dsl-card-generator/references/build_protocol.md +54 -0
  47. package/dist/vendored/skills/dsl-card-generator/references/component_rules.md +2 -2
  48. package/dist/vendored/skills/dsl-card-generator/references/converge_protocol.md +36 -0
  49. package/dist/vendored/skills/dsl-card-generator/references/event_patterns.md +1 -1
  50. package/dist/vendored/skills/dsl-card-generator/references/generation_pitfalls.md +1 -1
  51. package/dist/vendored/skills/dsl-card-generator/references/image_to_card.md +1 -1
  52. package/dist/vendored/skills/dsl-card-generator/references/quickref.md +1 -1
  53. package/dist/vendored/skills/dsl-card-generator/references/recipes.md +4 -4
  54. package/dist/vendored/skills/dsl-card-generator/references/rules.md +1 -1
  55. package/dist/vendored/skills/dsl-card-generator/references/slices_convention.md +6 -6
  56. package/dist/vendored/skills/dsl-card-generator/scripts/inventory_schemas.py +1 -1
  57. package/dist/vendored/skills/dsl-card-generator/scripts/lib/scaffold.mjs +1 -1
  58. package/dist/vendored/skills/dsl-card-generator/scripts/lib/slice_dedupe.mjs +2 -2
  59. package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-CeVX_y-M.js +137 -0
  60. package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-DiEiY59H.css +1 -0
  61. package/dist/vendored/skills/dsl-card-generator/vendor/preview/standalone.html +2 -2
  62. package/dist/vendored/skills/{finmall-card-sync → platform-card-sync}/SKILL.md +26 -26
  63. package/dist/vendored/skills/{finmall-card-sync → platform-card-sync}/scripts/api_batch_sync.mjs +184 -162
  64. package/dist/vendored/skills/{finmall-card-sync → platform-card-sync}/scripts/slice_upload.mjs +48 -29
  65. package/dist/vendored/skills/{finmall-session-navigator → platform-session-navigator}/SKILL.md +13 -13
  66. package/dist/vendored/skills/{finmall-session-navigator → platform-session-navigator}/scripts/package-lock.json +2 -2
  67. package/dist/vendored/skills/{finmall-session-navigator → platform-session-navigator}/scripts/package.json +2 -2
  68. package/dist/vendored/skills/{finmall-session-navigator/scripts/finmall_login.mjs → platform-session-navigator/scripts/platform_login.mjs} +41 -31
  69. package/dist/vendored/skills/{finmall-session-navigator → platform-session-navigator}/scripts/session-manager.mjs +17 -17
  70. package/dist/vendored/skills/shared/README.md +21 -21
  71. package/dist/vendored/skills/shared/auth-model.md +8 -10
  72. package/dist/vendored/skills/shared/{finmall-config.mjs → platform-config.mjs} +10 -10
  73. package/dist/vendored/tools/{finmall-env.mjs → platform-env.mjs} +13 -13
  74. package/package.json +1 -1
  75. package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-BCLshIzC.css +0 -1
  76. package/dist/vendored/skills/dsl-card-generator/vendor/preview/assets/standalone-pP5kTKiI.js +0 -88
@@ -0,0 +1,466 @@
1
+ /**
2
+ * 客户端产物编译(GaiaX 明文模板包)
3
+ *
4
+ * 1:1 迁移自 flame-release-mange(Java)的
5
+ * client/NativeEngine.java
6
+ * client/convert/{CardConvert,ComponentConvert,StyleConvert,PropertyConvert,
7
+ * EventActionConvert,DirectiveConvert,DataConvert,ConditionConvert}
8
+ * client/entity/*(TemplateEntity / JsonFileEntity / CssFileEntity / DataBindingFileEntity ...)
9
+ *
10
+ * 产物(每个模板一个目录):
11
+ * index.json 视图层级 { id, type:'gaia-template', sub-type?, package, layers }
12
+ * index.css `#nodeId { prop:value; }`
13
+ * index.databinding { data:{ nodeId:{...} }, event:{ nodeId:{...} } }
14
+ * index.data 卡片入参默认值(仅主模板)
15
+ * index.js Component({ onShow/onReady/... })
16
+ * index.style index.js 的 SM4-CBC 密文(iOS 侧优先读它,见 GXTemplateReader)
17
+ *
18
+ * 列表类组件(ListLayout / GridLayout)会拆成三级模板:
19
+ * <cardCode> ← 外层占位(grid 节点 + 属性绑定)
20
+ * <cardCode>_sub_<cid> ← 真正的 grid 模板(含 column 等)
21
+ * <cardCode>_sub_<cid>_item ← 循环项模板(子组件挂这里)
22
+ * 该结构由平台侧历史产物固化,客户端只认这套约定,不要简化成单层。
23
+ */
24
+
25
+ import { elementOf, styleConst, styleVar } from './clientElements.js'
26
+ import { convertCondition, convertPropsClient, convertExpression, hasText } from './common.js'
27
+
28
+ /* ------------------------------- 文件实体 ------------------------------- */
29
+
30
+ function createTemplate(code) {
31
+ return {
32
+ code,
33
+ cssRules: [], // [{ name, contents: [] }]
34
+ dataProps: {}, // index.data
35
+ dataBind: { data: {}, event: {} }, // index.databinding
36
+ js: { onShow: [], onDataInit: [], onReady: [], onHide: [], onReuse: [], onDestroy: [] },
37
+ json: { code: '', type: '', subType: '', packageInfo: {}, other: {}, layers: [] },
38
+ }
39
+ }
40
+
41
+ function addDataBind(template, componentId, map) {
42
+ const bucket = template.dataBind.data
43
+ bucket[componentId] = Object.assign(bucket[componentId] || {}, map)
44
+ }
45
+
46
+ function ensureDataBind(template, componentId) {
47
+ const bucket = template.dataBind.data
48
+ if (!bucket[componentId]) bucket[componentId] = {}
49
+ return bucket[componentId]
50
+ }
51
+
52
+ function addEventBind(template, componentId, map) {
53
+ const bucket = template.dataBind.event
54
+ bucket[componentId] = Object.assign(bucket[componentId] || {}, map)
55
+ }
56
+
57
+ const LIFECYCLES = ['onShow', 'onDataInit', 'onReady', 'onHide', 'onReuse', 'onDestroy']
58
+
59
+ function addLifecycleCode(template, lifeCycle, code) {
60
+ if (LIFECYCLES.includes(lifeCycle)) template.js[lifeCycle].push(code)
61
+ }
62
+
63
+ /* ------------------------------- 属性转换 ------------------------------- */
64
+
65
+ /** 常量域:标量 JSON 编码,Map/List 递归(与 Java convertConstField 等价) */
66
+ function normalizeConst(value) {
67
+ if (value === null || value === undefined) return undefined
68
+ if (Array.isArray(value)) return value.map((item) => (item && typeof item === 'object' ? normalizeConstMap(item) : item))
69
+ if (typeof value === 'object') return normalizeConstMap(value)
70
+ return JSON.stringify(value)
71
+ }
72
+
73
+ function normalizeConstMap(map) {
74
+ const out = {}
75
+ for (const [k, v] of Object.entries(map)) {
76
+ const normalized = normalizeConst(v)
77
+ if (normalized !== undefined) out[k] = normalized
78
+ }
79
+ return out
80
+ }
81
+
82
+ /**
83
+ * 变量域合并:字符串非空才写入(表达式为空表示「沿用静态值」),
84
+ * 对象/数组递归合并,保证同一节点既有静态也有动态属性时不互相覆盖。
85
+ */
86
+ function mergeVarField(target, key, value) {
87
+ if (value === null || value === undefined) return
88
+ if (Array.isArray(value)) {
89
+ const list = Array.isArray(target[key]) ? target[key] : []
90
+ target[key] = list
91
+ value.forEach((item, index) => {
92
+ if (!list[index]) list[index] = {}
93
+ if (item && typeof item === 'object') {
94
+ for (const [k, v] of Object.entries(item)) mergeVarField(list[index], k, v)
95
+ }
96
+ })
97
+ return
98
+ }
99
+ if (typeof value === 'object') {
100
+ const bucket = target[key] && typeof target[key] === 'object' ? target[key] : {}
101
+ target[key] = bucket
102
+ for (const [k, v] of Object.entries(value)) mergeVarField(bucket, k, v)
103
+ return
104
+ }
105
+ if (typeof value === 'string') {
106
+ if (hasText(value)) target[key] = value
107
+ return
108
+ }
109
+ target[key] = value
110
+ }
111
+
112
+ /** 组件属性 → databinding(静态走 const 归一化,动态覆盖) */
113
+ function convertComponentProps(component, template) {
114
+ const { const: constProps, var: varProps } = convertPropsClient(component.properties)
115
+ const out = normalizeConstMap(constProps)
116
+
117
+ for (const [key, value] of Object.entries(varProps)) mergeVarField(out, key, value)
118
+
119
+ addDataBind(template, component.componentId, out)
120
+ return { constProps, varProps, merged: out }
121
+ }
122
+
123
+ /* ------------------------------- 样式转换 ------------------------------- */
124
+
125
+ function convertStyle(className, style, template, componentId) {
126
+ const { const: constProps, var: varProps } = convertPropsClient(style.properties)
127
+
128
+ template.cssRules.push({ name: `#${componentId}`, contents: styleConst(className, constProps) })
129
+
130
+ const dynamic = {}
131
+ const vars = styleVar(varProps)
132
+ for (const [k, v] of Object.entries(vars)) {
133
+ if (hasText(v)) dynamic[k] = v
134
+ }
135
+ addDataBind(template, componentId, { extend: dynamic })
136
+ }
137
+
138
+ /* ------------------------------- 事件转换 ------------------------------- */
139
+
140
+ function convertEventBody(eventAction, component, engine, warnings) {
141
+ const actionType = eventAction.actionType
142
+ const { const: constProps } = convertPropsClient(eventAction.properties)
143
+ const desc = component ? elementOf(component.className) : null
144
+ let out = ''
145
+
146
+ // Canvas 的 onMeasured 前置:把画布尺寸换算进缩放坐标系
147
+ if (
148
+ component &&
149
+ desc &&
150
+ desc.type === 'canvas' &&
151
+ eventAction.eventKey === 'onMeasured' &&
152
+ actionType === 'customAction'
153
+ ) {
154
+ out +=
155
+ 'event.canvas = new Canvas(event.canvasId);' +
156
+ 'event.canvas.zoom = (event.screenWidth || 375)/375;' +
157
+ 'event.canvas.width = event.width/event.canvas.zoom;' +
158
+ 'event.canvas.height = event.height/event.canvas.zoom;' +
159
+ 'event.width = event.canvas.width;' +
160
+ 'event.height = event.canvas.height;'
161
+ }
162
+
163
+ if (actionType === 'customAction') {
164
+ const fn = constProps.function
165
+ const code = fn && typeof fn === 'object' ? fn.code : null
166
+ if (typeof code === 'string') out += code
167
+ else warnings.push(`事件 ${eventAction.eventKey} 的 customAction 缺少 function.code,已跳过`)
168
+ } else if (actionType === 'closeAlertCard') {
169
+ out += `ynet.callJSAPI('${actionType}',${JSON.stringify({ cardCode: engine.code })});`
170
+ } else if (hasText(actionType)) {
171
+ out += `ynet.callJSAPI('${actionType}',${JSON.stringify(constProps)});`
172
+ }
173
+
174
+ if (
175
+ component &&
176
+ desc &&
177
+ desc.type === 'canvas' &&
178
+ eventAction.eventKey === 'onMeasured' &&
179
+ actionType === 'customAction'
180
+ ) {
181
+ out += "\nevent.canvas.getContext('2d').endDraw();"
182
+ }
183
+
184
+ return out
185
+ }
186
+
187
+ function convertEventAction(eventAction, template, engine, component, warnings) {
188
+ if (!hasText(eventAction.eventKey) || !hasText(eventAction.actionType)) return
189
+
190
+ if (!component) {
191
+ // 卡片生命周期:包一层箭头函数隔离作用域,再在生命周期里立即执行
192
+ const body = convertEventBody(eventAction, null, engine, warnings)
193
+ addLifecycleCode(template, eventAction.eventKey, `((event) => {${body}\n})(event);`)
194
+ return
195
+ }
196
+
197
+ const cid = component.componentId
198
+ const body = convertEventBody(eventAction, component, engine, warnings)
199
+ const code =
200
+ `let ${cid}=this.getElementById('${cid}');` +
201
+ `if(${cid}){` +
202
+ `${cid}.addEventListener('${eventAction.eventKey}',(event)=>{${body}\n})` +
203
+ `}`
204
+ template.js.onReady.push(code)
205
+ }
206
+
207
+ /** 双向绑定:value 绑定到变量时补 input 监听,把输入写回状态 */
208
+ function convertTwoWayBinding(component, varProps, template) {
209
+ const desc = elementOf(component.className)
210
+ if (!desc.twoWayBinding) return
211
+ const modelVar = varProps.value
212
+ if (!hasText(modelVar)) return
213
+ const cid = component.componentId
214
+ template.js.onReady.push(
215
+ `let _${cid}_=this.getElementById('${cid}');` +
216
+ `if(_${cid}_){` +
217
+ `_${cid}_.addEventListener('input',(event)=>{` +
218
+ `this._syncData_(${JSON.stringify(modelVar)}, event)` +
219
+ `})` +
220
+ `}`
221
+ )
222
+ }
223
+
224
+ /* ------------------------------- 指令转换 ------------------------------- */
225
+
226
+ function convertDirectives(component, template, warnings) {
227
+ const { const: constProps } = convertPropsClient(component.directives)
228
+ const showProps = constProps['v-show']
229
+ if (!showProps || typeof showProps !== 'object') return
230
+
231
+ const showType = showProps.showType
232
+ if (showType === '00') return
233
+
234
+ const conditionMap = showProps.condition
235
+ if (!conditionMap || typeof conditionMap !== 'object') return
236
+
237
+ const bucket = ensureDataBind(template, component.componentId)
238
+ const extend = bucket.extend && typeof bucket.extend === 'object' ? bucket.extend : {}
239
+ bucket.extend = extend
240
+
241
+ const expression = conditionMap.expression
242
+ if (hasText(expression)) {
243
+ if (showType === '01') extend.display = `(${expression}) ? 'block' : 'none'`
244
+ else if (showType === '02') extend.display = `(${expression}) ? 'none' : 'block'`
245
+ return
246
+ }
247
+
248
+ if (Array.isArray(conditionMap.group) && conditionMap.group.length > 0) {
249
+ const expr = convertCondition(conditionMap.group)
250
+ if (showType === '01') extend.display = `${expr} ? 'block' : 'none'`
251
+ else if (showType === '02') extend.display = `${expr} ? 'none' : 'block'`
252
+ return
253
+ }
254
+
255
+ warnings.push(`组件 ${component.componentId} 的 v-show 条件为空,已忽略`)
256
+ }
257
+
258
+ /* -------------------------------- 引擎 --------------------------------- */
259
+
260
+ export function createClientCompiler(card, { version = '', warnings = [] } = {}) {
261
+ const engine = { code: card.code, version, templates: [] }
262
+
263
+ const newTemplate = () => {
264
+ const t = createTemplate('')
265
+ engine.templates.push(t)
266
+ return t
267
+ }
268
+
269
+ const convertBaseInfo = (code, subType, template, width) => {
270
+ template.json.code = code
271
+ template.json.type = 'gaia-template'
272
+ if (hasText(subType)) template.json.subType = subType
273
+ template.json.packageInfo = {
274
+ id: code,
275
+ version: engine.version,
276
+ dependencies: {},
277
+ }
278
+ if (width !== undefined) template.json.packageInfo.width = width
279
+ }
280
+
281
+ /* ---------------------------- 组件递归 ---------------------------- */
282
+
283
+ const convertArray = (components, template, layers) => {
284
+ for (const component of components) convert(component, template, layers)
285
+ }
286
+
287
+ const convert = (component, template, layers) => {
288
+ const desc = elementOf(component.className)
289
+ if (desc.unknown) warnings.push(`未识别的组件类型 ${component.className}(${component.componentId}),按 view 输出`)
290
+
291
+ if (desc.isNewTemplate) {
292
+ createComponentTemplate(component, template, layers, desc)
293
+ return
294
+ }
295
+ convertComponent(component, template, layers, desc)
296
+ }
297
+
298
+ const convertComponent = (component, template, layers, desc) => {
299
+ const layer = { id: component.componentId, type: desc.type, layers: [] }
300
+ layers.push(layer)
301
+
302
+ convertStyle(component.className, component.style, template, component.componentId)
303
+
304
+ const { varProps } = convertComponentProps(component, template)
305
+ convertTwoWayBinding(component, varProps, template)
306
+
307
+ for (const ea of component.eventactions) convertEventAction(ea, template, engine, component, warnings)
308
+
309
+ convertDirectives(component, template, warnings)
310
+
311
+ const slot = component.slots && component.slots[0]
312
+ if (slot) convertArray(slot.components, template, layer.layers)
313
+ }
314
+
315
+ /**
316
+ * 列表 / 网格:外层占位 + 子模板 + 循环项模板(三级)
317
+ * 注意:平台侧会把 componentId 改写成子模板编码,后续样式与绑定的 key 都用新编码。
318
+ */
319
+ const createComponentTemplate = (component, template, layers, desc) => {
320
+ const newTemplateCode = `${engine.code}_sub_${component.componentId}`
321
+ component.componentId = newTemplateCode
322
+
323
+ layers.push({ id: newTemplateCode, type: 'gaia-template', subType: 'custom', layers: [] })
324
+
325
+ convertComponentProps(component, template)
326
+ template.json.packageInfo.dependencies[newTemplateCode] = engine.version
327
+
328
+ const componentTemplate = newTemplate()
329
+ componentTemplate.code = newTemplateCode
330
+ addDataBind(componentTemplate, newTemplateCode, { value: '$$' })
331
+ convertBaseInfo(newTemplateCode, desc.type, componentTemplate)
332
+
333
+ convertTemplateComponent(component, componentTemplate, desc)
334
+ }
335
+
336
+ const convertTemplateComponent = (component, template, desc) => {
337
+ convertStyle(component.className, component.style, template, component.componentId)
338
+
339
+ const { const: constProps } = convertPropsClient(component.properties)
340
+ const constTransferred = desc.newTemplatePropsConst
341
+ ? desc.newTemplatePropsConst(constProps)
342
+ : constProps
343
+ for (const [k, v] of Object.entries(constTransferred)) template.json.other[k] = v
344
+ // 循环项数据由 databinding 的 `$$` 提供,模板顶层不保留 value
345
+ delete template.json.other.value
346
+
347
+ for (const ea of component.eventactions) convertEventAction(ea, template, engine, component, warnings)
348
+ convertDirectives(component, template, warnings)
349
+
350
+ const itemTemplateCode = `${component.componentId}_item`
351
+ template.json.layers.push({ id: itemTemplateCode, type: 'gaia-template', subType: 'custom', layers: [] })
352
+ template.json.packageInfo.dependencies[itemTemplateCode] = engine.version
353
+
354
+ const itemTemplate = newTemplate()
355
+ itemTemplate.code = itemTemplateCode
356
+ convertBaseInfo(itemTemplateCode, '', itemTemplate)
357
+
358
+ // 循环项样式:宽度由布局方向决定(横向列表 auto,其余撑满)
359
+ // 注意归属 —— 平台侧 StyleConvert.convertItem 收的是 itemComponentTemplate,
360
+ // 规则落在**第三层(_item)模板自己的 index.css** 里,不能挂到第二层。
361
+ if (desc.itemStyleConst) {
362
+ const contents = desc.itemStyleConst(constProps)
363
+ if (contents && contents.length) {
364
+ itemTemplate.cssRules.push({ name: `#${itemTemplateCode}`, contents })
365
+ }
366
+ }
367
+
368
+ const slot = component.slots && component.slots[0]
369
+ if (slot) convertArray(slot.components, itemTemplate, itemTemplate.json.layers)
370
+ }
371
+
372
+ /* ------------------------------ 卡片 ------------------------------ */
373
+
374
+ const convertCard = () => {
375
+ const template = newTemplate()
376
+
377
+ template.code = card.code
378
+ template.json.code = card.code
379
+ template.json.type = 'gaia-template'
380
+ template.json.packageInfo = {
381
+ id: card.code,
382
+ version: engine.version,
383
+ dependencies: {},
384
+ params: [],
385
+ }
386
+ if (card.width) template.json.packageInfo.width = card.width
387
+
388
+ // 数据定义:入参树进 package.params,默认值进 index.data
389
+ template.json.packageInfo.params = card.data.props.map(toParamField)
390
+ for (const field of card.data.props) {
391
+ template.dataProps[field.key] = field.defaultValue
392
+ }
393
+
394
+ convertStyle(card.className, card.style, template, card.code)
395
+
396
+ for (const ea of card.eventactions) convertEventAction(ea, template, engine, null, warnings)
397
+
398
+ convertArray(card.components, template, template.json.layers)
399
+ }
400
+
401
+ convertCard()
402
+ return engine
403
+ }
404
+
405
+ function toParamField(field) {
406
+ return {
407
+ name: field.key,
408
+ remark: field.name,
409
+ type: field.type,
410
+ child: (field.children || []).map(toParamField),
411
+ }
412
+ }
413
+
414
+ /* ------------------------------ 文件序列化 ------------------------------ */
415
+
416
+ export function serializeTemplateJson(template) {
417
+ const out = { ...template.json.other }
418
+ out.id = template.json.code
419
+ out.type = template.json.type
420
+ if (hasText(template.json.subType)) out['sub-type'] = template.json.subType
421
+ out.package = template.json.packageInfo
422
+ out.layers = serializeLayers(template.json.layers)
423
+ // 紧凑输出:平台侧走 fastjson 的 JSON.toJSONString,同样无缩进。
424
+ // 若这里加缩进,产物虽语义相同但字节全行不同,与线上产物对拍就失去意义。
425
+ return JSON.stringify(out)
426
+ }
427
+
428
+ function serializeLayers(layers) {
429
+ return layers.map((layer) => {
430
+ const out = { id: layer.id, type: layer.type }
431
+ if (hasText(layer.subType)) out['sub-type'] = layer.subType
432
+ if (layer.layers && layer.layers.length) out.layers = serializeLayers(layer.layers)
433
+ return out
434
+ })
435
+ }
436
+
437
+ export function serializeTemplateCss(template) {
438
+ return template.cssRules
439
+ .map((rule) => {
440
+ const body = rule.contents.map((line) => `${line};\n`).join('')
441
+ return `${rule.name}{\n${body}}\n`
442
+ })
443
+ .join('')
444
+ }
445
+
446
+ export function serializeTemplateDataBinding(template) {
447
+ const result = {}
448
+ if (Object.keys(template.dataBind.data).length) result.data = template.dataBind.data
449
+ if (Object.keys(template.dataBind.event).length) result.event = template.dataBind.event
450
+ return JSON.stringify(result)
451
+ }
452
+
453
+ export function serializeTemplateData(template) {
454
+ return JSON.stringify(template.dataProps)
455
+ }
456
+
457
+ export function serializeTemplateJs(template) {
458
+ let out = 'Component({'
459
+ for (const lifeCycle of LIFECYCLES) {
460
+ const codes = template.js[lifeCycle]
461
+ if (!codes.length) continue
462
+ out += `${lifeCycle}: function(event){${codes.join('')}},`
463
+ }
464
+ out += '});'
465
+ return out
466
+ }