diff --git a/docs/proposals/component-definition.zh.md b/docs/proposals/component-definition.zh.md index bbd1ed7..0fe59e6 100644 --- a/docs/proposals/component-definition.zh.md +++ b/docs/proposals/component-definition.zh.md @@ -479,8 +479,9 @@ attrs 遵守下面的不变量: - `class` 合并并去重; - Ebox style/layout attrs 先按 canonical property domain 去重,调用方值覆盖组件视觉 默认值; -- `role`、`ref`、`disabled`、`:on-*` 等 Runtime metadata 若与根 Host 已有值冲突, - 精确报错;需要由 Component 管理的 metadata 应声明为业务 prop; +- `role`、`ref`、`disabled`、`:on-*` 等 Runtime metadata 与普通前端 attrs 一样由 + 调用方覆盖根默认值;必须由 Component 掌控的 metadata 应显式声明为业务 prop, + 从 attrs 域中消费; - attrs 在调用方 Component 环境中求值,不能泄漏到被调用 Component 的 setup、 `etaf-state` 或业务 props; - 实际根 Host 仍通过 `ebox-style-property-accepted-p` 验证适用域,例如 Text 根不能 diff --git a/etaf-renderer.el b/etaf-renderer.el index e6717df..24f1d4a 100644 --- a/etaf-renderer.el +++ b/etaf-renderer.el @@ -144,10 +144,14 @@ SITE-TOKEN replaces PATH as the generated call-site identity when non-nil." (or (plist-get props :ref) (let ((site (or site-token (copy-sequence path)))) (list 'etaf-host - (if etaf--rendering-range-p + (cond + (etaf--rendering-range-p (list :range etaf--current-semantic-parent-id - :site site) - site))))) + :site site)) + (etaf--render-runtime + (list :parent etaf--current-semantic-parent-id :site site)) + (t + (list :path (copy-sequence path) :site site))))))) (defun etaf--merge-property (props key value) "Return PROPS with KEY set to VALUE, preserving the original order." diff --git a/etaf-view.el b/etaf-view.el index 1025ef2..b2437b7 100644 --- a/etaf-view.el +++ b/etaf-view.el @@ -287,9 +287,7 @@ Component semantics. COMPONENT-NAME labels diagnostics." (append (etaf--plist-remove-domain result key) (list key value)))) ((plist-member result key) - (etaf--component-error - "Component %S root metadata %S conflicts with fallthrough" - component-name key)) + (setq result (etaf--plist-set result key value))) (t (setq result (append result (list key value))))))) result)) diff --git a/tests/etaf-component-frontends-tests.el b/tests/etaf-component-frontends-tests.el index 46b09d9..79803c6 100644 --- a/tests/etaf-component-frontends-tests.el +++ b/tests/etaf-component-frontends-tests.el @@ -651,16 +651,31 @@ `((" *etaf-g6b-attrs-fragment*" ,(etaf-view (etaf-test-g6b-attr-fragment :color "red"))) (" *etaf-g6b-attrs-text*" - ,(etaf-view (etaf-test-g6b-attr-text :padding 1))) - (" *etaf-g6b-attrs-role*" - ,(etaf-view (etaf-test-g6b-attr-role :role 'navigation))))) + ,(etaf-view (etaf-test-g6b-attr-text :padding 1))))) (let ((buffer (car entry))) (unwind-protect (should-error (etaf-mount buffer (cadr entry)) :type 'etaf-component-call-error) (when-let* ((runtime (etaf-runtime-for-buffer buffer))) (etaf-unmount runtime)) - (when-let* ((live (get-buffer buffer))) (kill-buffer live)))))) + (when-let* ((live (get-buffer buffer))) (kill-buffer live))))) + (let ((buffer " *etaf-g6b-attrs-role*")) + (unwind-protect + (progn + (etaf-mount + buffer + (etaf-view + (etaf-test-g6b-attr-role + :role 'navigation :ref 'g6b-attrs-role))) + (should + (eq 'navigation + (plist-get + (etaf-runtime-host-props-for + (etaf-runtime-for-buffer buffer) 'g6b-attrs-role) + :role)))) + (when-let* ((runtime (etaf-runtime-for-buffer buffer))) + (etaf-unmount runtime)) + (when-let* ((live (get-buffer buffer))) (kill-buffer live))))) (ert-deftest etaf-component-host-attrs-rollback-root-shape-failure () "A later multi-root result cannot publish or retire the previous root." @@ -687,5 +702,22 @@ (etaf-unmount runtime)) (when-let* ((live (get-buffer buffer))) (kill-buffer live))))) +(ert-deftest etaf-component-compiled-host-sites-are-instance-scoped () + "Two instances of one compiled Component never share generated Host refs." + (let ((buffer " *etaf-g6b-instance-sites*")) + (unwind-protect + (progn + (etaf-mount + buffer + (etaf-view + (column + (etaf-test-g6b-attr-leaf :key 'one :label "One") + (etaf-test-g6b-attr-leaf :key 'two :label "Two")))) + (should (string-match-p + "One[[:space:]]+Two" (etaf-test-g6b--text buffer)))) + (when-let* ((runtime (etaf-runtime-for-buffer buffer))) + (etaf-unmount runtime)) + (when-let* ((live (get-buffer buffer))) (kill-buffer live))))) + (provide 'etaf-component-frontends-tests) ;;; etaf-component-frontends-tests.el ends here