cardbuddy 0.26.0 → 0.26.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/dist/preview-editor/assets/{index-CeoWrec1.css → index-BGqgV1Dw.css} +1 -1
- package/dist/preview-editor/assets/{index-Bn5gP68-.js → index-BOKHUeuy.js} +105 -103
- package/dist/preview-editor/index.html +2 -2
- package/dist/vendored/skills/dsl-card-generator/references/rules.md +1 -1
- package/dist/vendored/skills/dsl-card-generator/scripts/lint_card.mjs +27 -12
- package/package.json +1 -1
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16-Cg6Nrosk.png" />
|
|
9
9
|
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32-B_1744sp.png" />
|
|
10
10
|
<link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon-BQcES9QK.png" />
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-BOKHUeuy.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BGqgV1Dw.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="app"></div>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
- **Input 回写只写 rt.state**:Input 的 value 绑定 $key 时,渲染层兜底 `rt.state[key] = v`(**只写 rt.state、绝不写 rt.rootScope**,故打字不触发"保存到本地"脏标记)。
|
|
46
46
|
- **读写约定**:运行态变量(含用户输入/绑定派生值)一律用 this.state 读、写走 this.setData;this.data 仅用于读 Input 绑定键的实时输入值;持久卡片态存 this.state;任何应重渲染的改动后调 this.setData(...)。**调试交互绝不调 setRootData**——否则会污染"保存到本地"基线。
|
|
47
47
|
- **事件代码必须 `var self = this` 起手(指针安全铁律)**:每个 customAction 的 `function.code` / `compiled` **第一行**写 `var self = this;`。**任何嵌套函数(addEventListener 回调、`Array.map/forEach/find` 回调、setTimeout、ynet.callJSAPI 回调等)里访问组件实例一律用 `self`,禁止用 `this`**——`this` 在回调里会被运行时重绑(指向全局 / 事件源 / undefined),直接 `this.setData` 会指向错误对象,导致静默失效或 `this is undefined` 报错。顶层(非嵌套)直接调用可用 `this`,但统一用 `self` 最稳、零歧义。**生成事件代码默认就按此写,不要等出错再改。** export-proven:holdFinancialProducts 根 `onReady` 写 `var self = this; ... this.addEventListener('globalSet', (e) => self.$set(e.data.key, e.data.value))`;recipes.md §4 / §7 同构。
|
|
48
|
-
- **List/Grid item 事件改绑定数据必须 globalSet 透传(铁律)**:ListLayout/GridLayout 的 item(slotScope)事件里禁止直接改绑定数据(this.setData/setRootData/直接 state 赋值)——必须 `this.postMessage('globalSet', patch)`(patch 与 setData 入参逐字一致),并先在根节点 onReady `addEventListener('globalSet')` 注册监听、监听里用闭包 self 代 `setData(patch)`;其他节点事件可直接 setData。lint `r54` ERROR 拦截「item 直改」与「用 globalSet
|
|
48
|
+
- **List/Grid item 事件改绑定数据必须 globalSet 透传(铁律)**:ListLayout/GridLayout 的 item(slotScope)事件里禁止直接改绑定数据(this.setData/setRootData/直接 state 赋值)——必须 `this.postMessage('globalSet', patch)`(patch 与 setData 入参逐字一致),并先在根节点 onReady `addEventListener('globalSet')` 注册监听、监听里用闭包 self 代 `setData(patch)`;其他节点事件可直接 setData。lint `r54` ERROR 拦截「item 直改」与「用 globalSet 但根无监听」;判定覆盖 item 模板(`slotScope`)子树内的**所有节点**(不止 item 根),`that` / `self` / `_this` 别名与 `this` 等价计入。固定范式见 recipes.md §4 / recipe_snippets/globalSet.json。
|
|
49
49
|
- **slotScope item 字段在事件脚本里直读(铁律 r55)**:slotScope:true 的 List/Grid item 事件脚本里,当前 item 字段**直接 `this.state.<field>` 读取**(如 `var q = this.state.query;`)——作用域展开是平台保证,**不是**需要兜底判断的不确定行为;禁止写 `if (x === undefined && this.state.$$)` 之类的「$$ 兜底」。事件脚本(customAction)里**禁止出现任何 `$$`**(含 `this.state.$$`、`$$.field`);`$$` 只在**表达式**里引用整个 item 时才允许(R50 另管 `$$.field` 冗余)。lint `r55-event-no-dollar-dollar` ERROR 拦截。
|
|
50
50
|
- **调 JSAPI 直接裸调 ynet(铁律 r56)**:事件脚本里调 JSAPI 一律 `ynet.callJSAPI(name, params, cb)`——ynet 由平台注入为全局;**禁止 `window.ynet` 前缀**,也**禁止 `typeof window !== 'undefined' && window.ynet && window.ynet.callJSAPI` 之类存在性守卫**(把平台保证误当不确定行为的冗余错写;49 张导出卡全量裸调为证)。lint `r56-jsapi-no-window-guard` ERROR 拦截。
|
|
51
51
|
- **生成代码必须严格带 `;`(语句结束符不省略)**:customAction 的 `function.code` / `compiled` 里**每条 JS 语句结尾都要写分号,禁止依赖 ASI(自动分号插入)省略**。`return` / 换行 / 对象字面量 / 数组字面量等场景省略分号会触发 ASI 陷阱(如 `return\n{...}` 被解析成 `return;` 直接返回 undefined);且导出卡(49+ 卡 `compiled` 全量带分号)风格不一致会让 `diff_card.mjs` 更难比对。**生成时默认每条语句结尾补 `;`,不要"省着写"。** export-proven:holdFinancialProducts / creditCardBill 等所有导出卡 `compiled` 均全量带分号。
|
|
@@ -101,11 +101,25 @@ Options:
|
|
|
101
101
|
/* ------------------------------------------------------------------ */
|
|
102
102
|
/* 节点遍历 */
|
|
103
103
|
/* ------------------------------------------------------------------ */
|
|
104
|
-
|
|
104
|
+
/**
|
|
105
|
+
* slot.slotScope 判定:正常是布尔 true,但存量卡里有 2 处写成字符串 'true',一并兼容
|
|
106
|
+
*/
|
|
107
|
+
function isSlotScope(slot) {
|
|
108
|
+
const v = (slot || {}).slotScope
|
|
109
|
+
return v === true || v === 'true'
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {object} node
|
|
114
|
+
* @param {(node:object, parent:object|null, inSlotScope:boolean)=>void} visitor
|
|
115
|
+
* @param {object|null} parent 父节点(实参传递,不往 schema 上挂字段,避免污染 JSON.stringify)
|
|
116
|
+
* @param {boolean} inSlotScope 是否位于 ListLayout/GridLayout 的 item 模板(slot.slotScope === true)子树内
|
|
117
|
+
*/
|
|
118
|
+
function walk(node, visitor, parent = null, inSlotScope = false) {
|
|
105
119
|
if (!node || typeof node !== 'object') return
|
|
106
|
-
visitor(node, parent)
|
|
107
|
-
if (Array.isArray(node.components)) for (const c of node.components) walk(c, visitor, node)
|
|
108
|
-
for (const s of node.slots || []) for (const c of s.components || []) walk(c, visitor, node)
|
|
120
|
+
visitor(node, parent, inSlotScope)
|
|
121
|
+
if (Array.isArray(node.components)) for (const c of node.components) walk(c, visitor, node, inSlotScope)
|
|
122
|
+
for (const s of node.slots || []) for (const c of s.components || []) walk(c, visitor, node, inSlotScope || isSlotScope(s))
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
/** 获取默认插槽直接子节点(兼容 Root.components 与普通节点 slots[0].components) */
|
|
@@ -1329,13 +1343,14 @@ export function lint(schema, data) {
|
|
|
1329
1343
|
const f = fn.find((x) => x.itemKey === 'function') || {}
|
|
1330
1344
|
return String((f.itemValue || {}).code || (f.itemValue || {}).compiled || '')
|
|
1331
1345
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1346
|
+
// 别名与 collectBindings 保持一致:脚本里常用 that/self/_this 承接 this(部分由低代码编译器生成)
|
|
1347
|
+
const ALIAS = '(?:this|self|that|_this)'
|
|
1348
|
+
const MUTATE_RE = new RegExp(
|
|
1349
|
+
`\\b${ALIAS}\\s*\\.\\s*(?:setData|setRootData)\\s*\\(`
|
|
1350
|
+
+ `|\\b${ALIAS}\\s*\\.\\s*(?:state|data)(?:\\.[\\w$]+)+\\s*=[^=]`,
|
|
1351
|
+
)
|
|
1352
|
+
walk(view, (n, parent, inSlotScope) => {
|
|
1353
|
+
if (!inSlotScope) return
|
|
1339
1354
|
if (n.className === 'ListLayout' || n.className === 'GridLayout') return
|
|
1340
1355
|
const cid = n.componentId || '(root)'
|
|
1341
1356
|
for (const ea of n.eventactions || []) {
|
|
@@ -1350,7 +1365,7 @@ export function lint(schema, data) {
|
|
|
1350
1365
|
})
|
|
1351
1366
|
}
|
|
1352
1367
|
}
|
|
1353
|
-
}
|
|
1368
|
+
})
|
|
1354
1369
|
// R54-b: 用了 globalSet 透传但根节点 onReady 没注册监听 → 消息石沉大海
|
|
1355
1370
|
const allCode = []
|
|
1356
1371
|
let rootOnReadyCode = ''
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cardbuddy",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "Local dev service for the CardBuddy card editor — decouples card CRUD (sync/save/validate/render-status/screenshot) from the static public editor, so the editor can be deployed to any static host while each developer runs a local service against their own card source.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|