@domql/utils 3.6.1 → 3.6.4

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 ADDED
@@ -0,0 +1,77 @@
1
+ # @domql/utils
2
+
3
+ Core utility functions for the DOMQL element system.
4
+
5
+ ## Propertization (`props.js`)
6
+
7
+ The propertization system normalizes element definitions by sorting keys between the element root (child elements, framework keys) and `props` (CSS properties, design tokens, custom data).
8
+
9
+ ### Two-phase process
10
+
11
+ 1. **`pickupPropsFromElement`** — Scans element root keys and moves non-element, non-builtin keys into `props`
12
+ 2. **`pickupElementFromProps`** — Scans `props` keys and moves element-like or builtin keys back to the element root
13
+
14
+ ### Key classification rules
15
+
16
+ | Pattern | Classification | Example |
17
+ |---------|---------------|---------|
18
+ | Starts with uppercase | Child element | `Header`, `Button`, `Nav` |
19
+ | Numeric key | Child element | `0`, `1`, `2` |
20
+ | In `DOMQ_PROPERTIES` | Framework builtin | `tag`, `extends`, `on` |
21
+ | In `CSS_SELECTOR_PREFIXES` | CSS-in-props | `:hover`, `@mobileS`, `$isActive` |
22
+ | Has define handler | Define key (stays at root) | `$router`, deprecated: `$propsCollection` |
23
+ | `childProps` | Always in props | `childProps` |
24
+ | `onXxx` + function | Event handler | `onClick`, `onSubmit` |
25
+ | Everything else | Prop | `padding`, `theme`, `color` |
26
+
27
+ ### CSS_SELECTOR_PREFIXES
28
+
29
+ ```javascript
30
+ const CSS_SELECTOR_PREFIXES = new Set([
31
+ ':', '@', '[', '*', '+', '~', '&', '>', '$', '-', '.', '!'
32
+ ])
33
+ ```
34
+
35
+ These single-character prefixes identify keys that should be processed by `css-in-props` via `transformersByPrefix`. When a key starts with one of these characters, it gets moved into `props`.
36
+
37
+ ### Define-awareness
38
+
39
+ The `$` prefix is shared between css-in-props conditionals (`$isActive`) and define handlers (built-in `$router`, plus deprecated v2 handlers like `$propsCollection`, `$collection` that some older projects still use). The propertization must check for define handlers **before** applying prefix rules:
40
+
41
+ ```javascript
42
+ // Check define handlers first — these stay at root
43
+ const defineValue = this.define?.[key]
44
+ const globalDefineValue = this.context?.define?.[key]
45
+ if (isFunction(defineValue) || isFunction(globalDefineValue)) continue
46
+
47
+ // Only then apply prefix-based classification
48
+ if (CSS_SELECTOR_PREFIXES.has(firstChar)) {
49
+ obj.props[key] = value
50
+ }
51
+ ```
52
+
53
+ Without this check, define keys like `$router` (or deprecated `$propsCollection` in older projects) get moved into `props` and become invisible to `throughInitialDefine`, breaking routing and collection-based rendering.
54
+
55
+ ### childProps handling
56
+
57
+ `childProps` is a framework property that configures child element props. It must always stay in `props` (consumed by `inheritParentProps`) even though its value may contain uppercase keys that look like child elements:
58
+
59
+ ```javascript
60
+ // childProps: { Icon: { name: 'star' }, Hgroup: { ... } }
61
+ // The uppercase keys inside are NOT child elements
62
+ if (key === 'childProps') {
63
+ obj.props[key] = value
64
+ delete obj[key]
65
+ continue
66
+ }
67
+ ```
68
+
69
+ ### ignoreChildProps
70
+
71
+ When set on `element.props`, prevents the element from inheriting `childProps` from its parent via `inheritParentProps`. Used by fragment elements to avoid double-application of childProps (since fragments explicitly forward childProps to their children).
72
+
73
+ ## Key Sets (`keys.js`)
74
+
75
+ - **`DOMQ_PROPERTIES`** — Framework-level keys that stay at element root
76
+ - **`PROPS_METHODS`** — Keys on the props prototype (update, __element)
77
+ - **`STATE_METHODS`** — State management methods (update, parse, set, toggle, etc.)
package/dist/cjs/props.js CHANGED
@@ -43,6 +43,7 @@ var import_types = require("./types.js");
43
43
  var import_string = require("./string.js");
44
44
  const RE_UPPER = /^[A-Z]/;
45
45
  const RE_DIGITS = /^\d+$/;
46
+ const CSS_SELECTOR_PREFIXES = /* @__PURE__ */ new Set([":", "@", "[", "*", "+", "~", "&", ">", "$", "-", ".", "!"]);
46
47
  const ELEMENT_INDICATOR_KEYS = /* @__PURE__ */ new Set([
47
48
  "extend",
48
49
  "props",
@@ -89,11 +90,27 @@ function pickupPropsFromElement(obj, opts = {}) {
89
90
  delete obj[key];
90
91
  continue;
91
92
  }
92
- const hasDefine = (0, import_types.isObject)(this.define?.[key]);
93
- const hasGlobalDefine = (0, import_types.isObject)(this.context?.define?.[key]);
93
+ if (key === "childProps") {
94
+ obj.props[key] = value;
95
+ delete obj[key];
96
+ cachedKeys.push(key);
97
+ continue;
98
+ }
99
+ const defineValue = this.define?.[key];
100
+ const globalDefineValue = this.context?.define?.[key];
101
+ const hasDefine = (0, import_types.isObject)(defineValue) || (0, import_types.isFunction)(defineValue);
102
+ const hasGlobalDefine = (0, import_types.isObject)(globalDefineValue) || (0, import_types.isFunction)(globalDefineValue);
103
+ if (hasDefine || hasGlobalDefine) continue;
104
+ const firstChar = key.charAt(0);
105
+ if (CSS_SELECTOR_PREFIXES.has(firstChar)) {
106
+ obj.props[key] = value;
107
+ delete obj[key];
108
+ cachedKeys.push(key);
109
+ continue;
110
+ }
94
111
  const isElement = RE_UPPER.test(key) || RE_DIGITS.test(key) || looksLikeElement(value);
95
112
  const isBuiltin = import_keys.DOMQ_PROPERTIES.has(key);
96
- if (!isElement && !isBuiltin && !hasDefine && !hasGlobalDefine) {
113
+ if (!isElement && !isBuiltin) {
97
114
  obj.props[key] = value;
98
115
  delete obj[key];
99
116
  cachedKeys.push(key);
@@ -113,8 +130,13 @@ function pickupElementFromProps(obj = this, opts) {
113
130
  continue;
114
131
  }
115
132
  if (cachedKeys.includes(key)) continue;
116
- const hasDefine = (0, import_types.isObject)(this.define?.[key]);
117
- const hasGlobalDefine = (0, import_types.isObject)(this.context?.define?.[key]);
133
+ if (key === "childProps") continue;
134
+ const firstChar = key.charAt(0);
135
+ if (CSS_SELECTOR_PREFIXES.has(firstChar)) continue;
136
+ const defineValue = this.define?.[key];
137
+ const globalDefineValue = this.context?.define?.[key];
138
+ const hasDefine = (0, import_types.isObject)(defineValue) || (0, import_types.isFunction)(defineValue);
139
+ const hasGlobalDefine = (0, import_types.isObject)(globalDefineValue) || (0, import_types.isFunction)(globalDefineValue);
118
140
  const isElement = RE_UPPER.test(key) || RE_DIGITS.test(key);
119
141
  const isBuiltin = import_keys.DOMQ_PROPERTIES.has(key);
120
142
  if (isElement || isBuiltin || hasDefine || hasGlobalDefine) {
@@ -153,7 +175,7 @@ const inheritParentProps = (element, parent) => {
153
175
  const parentProps = parent.props;
154
176
  if (!parentProps) return propsStack;
155
177
  const matchParentKeyProps = parentProps[element.key];
156
- const matchParentChildProps = parentProps.childProps;
178
+ const matchParentChildProps = parentProps.childProps || parent.childProps;
157
179
  const ignoreChildProps = element.props?.ignoreChildProps;
158
180
  if (matchParentChildProps && !ignoreChildProps) {
159
181
  const childProps = objectizeStringProperty(matchParentChildProps);
package/dist/esm/props.js CHANGED
@@ -5,6 +5,7 @@ import { is, isArray, isFunction, isObject, isObjectLike } from "./types.js";
5
5
  import { lowercaseFirstLetter } from "./string.js";
6
6
  const RE_UPPER = /^[A-Z]/;
7
7
  const RE_DIGITS = /^\d+$/;
8
+ const CSS_SELECTOR_PREFIXES = /* @__PURE__ */ new Set([":", "@", "[", "*", "+", "~", "&", ">", "$", "-", ".", "!"]);
8
9
  const ELEMENT_INDICATOR_KEYS = /* @__PURE__ */ new Set([
9
10
  "extend",
10
11
  "props",
@@ -51,11 +52,27 @@ function pickupPropsFromElement(obj, opts = {}) {
51
52
  delete obj[key];
52
53
  continue;
53
54
  }
54
- const hasDefine = isObject(this.define?.[key]);
55
- const hasGlobalDefine = isObject(this.context?.define?.[key]);
55
+ if (key === "childProps") {
56
+ obj.props[key] = value;
57
+ delete obj[key];
58
+ cachedKeys.push(key);
59
+ continue;
60
+ }
61
+ const defineValue = this.define?.[key];
62
+ const globalDefineValue = this.context?.define?.[key];
63
+ const hasDefine = isObject(defineValue) || isFunction(defineValue);
64
+ const hasGlobalDefine = isObject(globalDefineValue) || isFunction(globalDefineValue);
65
+ if (hasDefine || hasGlobalDefine) continue;
66
+ const firstChar = key.charAt(0);
67
+ if (CSS_SELECTOR_PREFIXES.has(firstChar)) {
68
+ obj.props[key] = value;
69
+ delete obj[key];
70
+ cachedKeys.push(key);
71
+ continue;
72
+ }
56
73
  const isElement = RE_UPPER.test(key) || RE_DIGITS.test(key) || looksLikeElement(value);
57
74
  const isBuiltin = DOMQ_PROPERTIES.has(key);
58
- if (!isElement && !isBuiltin && !hasDefine && !hasGlobalDefine) {
75
+ if (!isElement && !isBuiltin) {
59
76
  obj.props[key] = value;
60
77
  delete obj[key];
61
78
  cachedKeys.push(key);
@@ -75,8 +92,13 @@ function pickupElementFromProps(obj = this, opts) {
75
92
  continue;
76
93
  }
77
94
  if (cachedKeys.includes(key)) continue;
78
- const hasDefine = isObject(this.define?.[key]);
79
- const hasGlobalDefine = isObject(this.context?.define?.[key]);
95
+ if (key === "childProps") continue;
96
+ const firstChar = key.charAt(0);
97
+ if (CSS_SELECTOR_PREFIXES.has(firstChar)) continue;
98
+ const defineValue = this.define?.[key];
99
+ const globalDefineValue = this.context?.define?.[key];
100
+ const hasDefine = isObject(defineValue) || isFunction(defineValue);
101
+ const hasGlobalDefine = isObject(globalDefineValue) || isFunction(globalDefineValue);
80
102
  const isElement = RE_UPPER.test(key) || RE_DIGITS.test(key);
81
103
  const isBuiltin = DOMQ_PROPERTIES.has(key);
82
104
  if (isElement || isBuiltin || hasDefine || hasGlobalDefine) {
@@ -115,7 +137,7 @@ const inheritParentProps = (element, parent) => {
115
137
  const parentProps = parent.props;
116
138
  if (!parentProps) return propsStack;
117
139
  const matchParentKeyProps = parentProps[element.key];
118
- const matchParentChildProps = parentProps.childProps;
140
+ const matchParentChildProps = parentProps.childProps || parent.childProps;
119
141
  const ignoreChildProps = element.props?.ignoreChildProps;
120
142
  if (matchParentChildProps && !ignoreChildProps) {
121
143
  const childProps = objectizeStringProperty(matchParentChildProps);
@@ -2004,6 +2004,7 @@ var DomqlUtils = (() => {
2004
2004
  // props.js
2005
2005
  var RE_UPPER = /^[A-Z]/;
2006
2006
  var RE_DIGITS = /^\d+$/;
2007
+ var CSS_SELECTOR_PREFIXES = /* @__PURE__ */ new Set([":", "@", "[", "*", "+", "~", "&", ">", "$", "-", ".", "!"]);
2007
2008
  var ELEMENT_INDICATOR_KEYS = /* @__PURE__ */ new Set([
2008
2009
  "extend",
2009
2010
  "props",
@@ -2050,11 +2051,27 @@ var DomqlUtils = (() => {
2050
2051
  delete obj[key];
2051
2052
  continue;
2052
2053
  }
2053
- const hasDefine = isObject(this.define?.[key]);
2054
- const hasGlobalDefine = isObject(this.context?.define?.[key]);
2054
+ if (key === "childProps") {
2055
+ obj.props[key] = value;
2056
+ delete obj[key];
2057
+ cachedKeys.push(key);
2058
+ continue;
2059
+ }
2060
+ const defineValue = this.define?.[key];
2061
+ const globalDefineValue = this.context?.define?.[key];
2062
+ const hasDefine = isObject(defineValue) || isFunction(defineValue);
2063
+ const hasGlobalDefine = isObject(globalDefineValue) || isFunction(globalDefineValue);
2064
+ if (hasDefine || hasGlobalDefine) continue;
2065
+ const firstChar = key.charAt(0);
2066
+ if (CSS_SELECTOR_PREFIXES.has(firstChar)) {
2067
+ obj.props[key] = value;
2068
+ delete obj[key];
2069
+ cachedKeys.push(key);
2070
+ continue;
2071
+ }
2055
2072
  const isElement = RE_UPPER.test(key) || RE_DIGITS.test(key) || looksLikeElement(value);
2056
2073
  const isBuiltin = DOMQ_PROPERTIES.has(key);
2057
- if (!isElement && !isBuiltin && !hasDefine && !hasGlobalDefine) {
2074
+ if (!isElement && !isBuiltin) {
2058
2075
  obj.props[key] = value;
2059
2076
  delete obj[key];
2060
2077
  cachedKeys.push(key);
@@ -2074,8 +2091,13 @@ var DomqlUtils = (() => {
2074
2091
  continue;
2075
2092
  }
2076
2093
  if (cachedKeys.includes(key)) continue;
2077
- const hasDefine = isObject(this.define?.[key]);
2078
- const hasGlobalDefine = isObject(this.context?.define?.[key]);
2094
+ if (key === "childProps") continue;
2095
+ const firstChar = key.charAt(0);
2096
+ if (CSS_SELECTOR_PREFIXES.has(firstChar)) continue;
2097
+ const defineValue = this.define?.[key];
2098
+ const globalDefineValue = this.context?.define?.[key];
2099
+ const hasDefine = isObject(defineValue) || isFunction(defineValue);
2100
+ const hasGlobalDefine = isObject(globalDefineValue) || isFunction(globalDefineValue);
2079
2101
  const isElement = RE_UPPER.test(key) || RE_DIGITS.test(key);
2080
2102
  const isBuiltin = DOMQ_PROPERTIES.has(key);
2081
2103
  if (isElement || isBuiltin || hasDefine || hasGlobalDefine) {
@@ -2114,7 +2136,7 @@ var DomqlUtils = (() => {
2114
2136
  const parentProps = parent.props;
2115
2137
  if (!parentProps) return propsStack;
2116
2138
  const matchParentKeyProps = parentProps[element.key];
2117
- const matchParentChildProps = parentProps.childProps;
2139
+ const matchParentChildProps = parentProps.childProps || parent.childProps;
2118
2140
  const ignoreChildProps = element.props?.ignoreChildProps;
2119
2141
  if (matchParentChildProps && !ignoreChildProps) {
2120
2142
  const childProps = objectizeStringProperty(matchParentChildProps);
package/extends.js CHANGED
@@ -334,7 +334,7 @@ export const getExtendsInElement = obj => {
334
334
  function traverse (o) {
335
335
  for (const key in o) {
336
336
  if (Object.prototype.hasOwnProperty.call(o, key)) {
337
- // Check if the key starts with a capital letter and exclude keys like @mobileL, $propsCollection
337
+ // Check if the key starts with a capital letter and exclude keys like @mobileL, $router
338
338
  if (matchesComponentNaming(key)) {
339
339
  result.push(key)
340
340
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@domql/utils",
3
- "version": "3.6.1",
3
+ "version": "3.6.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "module": "./dist/esm/index.js",
package/props.js CHANGED
@@ -9,6 +9,10 @@ import { lowercaseFirstLetter } from './string.js'
9
9
  const RE_UPPER = /^[A-Z]/
10
10
  const RE_DIGITS = /^\d+$/
11
11
 
12
+ // Characters that mark css-in-props selectors (pseudo-selectors, media queries, etc.)
13
+ // These properties must always live in props so useCssInProps() can process them
14
+ const CSS_SELECTOR_PREFIXES = new Set([':', '@', '[', '*', '+', '~', '&', '>', '$', '-', '.', '!'])
15
+
12
16
  const ELEMENT_INDICATOR_KEYS = new Set([
13
17
  'extend', 'props', 'text', 'tag', 'on', 'if', 'childExtend',
14
18
  'children', 'childrenAs', 'state', 'html', 'attr',
@@ -50,13 +54,38 @@ export function pickupPropsFromElement (obj, opts = {}) {
50
54
  continue
51
55
  }
52
56
 
53
- const hasDefine = isObject(this.define?.[key])
54
- const hasGlobalDefine = isObject(this.context?.define?.[key])
57
+ // childProps is a framework property that must always live in props
58
+ // even though its value may contain uppercase keys (Icon, Hgroup, etc.)
59
+ if (key === 'childProps') {
60
+ obj.props[key] = value
61
+ delete obj[key]
62
+ cachedKeys.push(key)
63
+ continue
64
+ }
65
+
66
+ // Check define handlers - keys with define handlers must stay at root
67
+ // for throughInitialDefine to process them
68
+ const defineValue = this.define?.[key]
69
+ const globalDefineValue = this.context?.define?.[key]
70
+ const hasDefine = isObject(defineValue) || isFunction(defineValue)
71
+ const hasGlobalDefine = isObject(globalDefineValue) || isFunction(globalDefineValue)
72
+ if (hasDefine || hasGlobalDefine) continue
73
+
74
+ // CSS-in-props selectors (:after, :hover, @mobileS, etc.) must always
75
+ // live in props so useCssInProps() can process them via transformersByPrefix
76
+ const firstChar = key.charAt(0)
77
+ if (CSS_SELECTOR_PREFIXES.has(firstChar)) {
78
+ obj.props[key] = value
79
+ delete obj[key]
80
+ cachedKeys.push(key)
81
+ continue
82
+ }
83
+
55
84
  const isElement = RE_UPPER.test(key) || RE_DIGITS.test(key) || looksLikeElement(value)
56
85
  const isBuiltin = DOMQ_PROPERTIES.has(key)
57
86
 
58
87
  // If it's not a special case, move to props
59
- if (!isElement && !isBuiltin && !hasDefine && !hasGlobalDefine) {
88
+ if (!isElement && !isBuiltin) {
60
89
  obj.props[key] = value
61
90
  delete obj[key]
62
91
  cachedKeys.push(key)
@@ -85,8 +114,17 @@ export function pickupElementFromProps (obj = this, opts) {
85
114
  // Skip if key was originally from obj
86
115
  if (cachedKeys.includes(key)) continue
87
116
 
88
- const hasDefine = isObject(this.define?.[key])
89
- const hasGlobalDefine = isObject(this.context?.define?.[key])
117
+ // childProps must stay in props - it's consumed by inheritParentProps
118
+ if (key === 'childProps') continue
119
+
120
+ // CSS-in-props selectors must stay in props
121
+ const firstChar = key.charAt(0)
122
+ if (CSS_SELECTOR_PREFIXES.has(firstChar)) continue
123
+
124
+ const defineValue = this.define?.[key]
125
+ const globalDefineValue = this.context?.define?.[key]
126
+ const hasDefine = isObject(defineValue) || isFunction(defineValue)
127
+ const hasGlobalDefine = isObject(globalDefineValue) || isFunction(globalDefineValue)
90
128
  const isElement = RE_UPPER.test(key) || RE_DIGITS.test(key)
91
129
  const isBuiltin = DOMQ_PROPERTIES.has(key)
92
130
 
@@ -140,7 +178,7 @@ export const inheritParentProps = (element, parent) => {
140
178
  if (!parentProps) return propsStack
141
179
 
142
180
  const matchParentKeyProps = parentProps[element.key]
143
- const matchParentChildProps = parentProps.childProps
181
+ const matchParentChildProps = parentProps.childProps || parent.childProps
144
182
 
145
183
  // Order matters: key-specific props should be added after childProps
146
184
  const ignoreChildProps = element.props?.ignoreChildProps