druxt 0.24.1-dev.20260920213131 → 0.24.1-dev.20260921024946

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.
@@ -5,6 +5,66 @@ import Vue from 'vue'
5
5
 
6
6
  import DruxtWrapper from './DruxtWrapper.vue'
7
7
 
8
+ /**
9
+ * Whether JSON holds a value well enough to compare two of them.
10
+ *
11
+ * A function or a symbol writes as `undefined`, a Map or a Set as `{}`, and
12
+ * NaN as `null`, so two different values can write the same text. Anything
13
+ * JSON does not hold exactly is compared by identity instead.
14
+ *
15
+ * @private
16
+ *
17
+ * @param {*} value - The value to check.
18
+ * @param {WeakSet} [seen] - Objects already visited, so a cycle ends the walk.
19
+ *
20
+ * @returns {boolean} Whether JSON.stringify represents the value exactly.
21
+ */
22
+ const isComparable = (value, seen = new WeakSet()) => {
23
+ if (value === null) return true
24
+
25
+ const type = typeof value
26
+ if (type === 'string' || type === 'boolean') return true
27
+ if (type === 'number') return Number.isFinite(value)
28
+ if (type !== 'object') return false
29
+
30
+ if (seen.has(value)) return false
31
+ seen.add(value)
32
+
33
+ if (Array.isArray(value)) return value.every((item) => isComparable(item, seen))
34
+
35
+ // A Map, a Set, a Date or any class instance writes as something other than itself.
36
+ const prototype = Object.getPrototypeOf(value)
37
+ if (prototype !== Object.prototype && prototype !== null) return false
38
+
39
+ return Object.values(value).every((item) => isComparable(item, seen))
40
+ }
41
+
42
+ /**
43
+ * Whether rebuilt propsData differs from what the wrapper already has.
44
+ *
45
+ * Values that cannot be compared count as changed, so a rebuild is skipped
46
+ * only when the two are known to match.
47
+ *
48
+ * @private
49
+ *
50
+ * @param {object} current - The propsData the wrapper holds.
51
+ * @param {object} next - The propsData the module's callback just produced.
52
+ *
53
+ * @returns {boolean} Whether the wrapper needs the new propsData.
54
+ */
55
+ const hasChanged = (current = {}, next = {}) => {
56
+ const keys = [...new Set([...Object.keys(current), ...Object.keys(next)])]
57
+ return keys.some((key) => {
58
+ if (current[key] === next[key]) return false
59
+ if (!isComparable(current[key]) || !isComparable(next[key])) return true
60
+ try {
61
+ return JSON.stringify(current[key]) !== JSON.stringify(next[key])
62
+ } catch (err) {
63
+ return true
64
+ }
65
+ })
66
+ }
67
+
8
68
  /**
9
69
  * The DruxtModule component renders a Druxt module; import and extend the
10
70
  * component to build your own module.
@@ -168,6 +228,9 @@ export default {
168
228
 
169
229
  // Get wrapper data.
170
230
  const wrapperData = await this.getWrapperData(component.is)
231
+ // On the instance rather than in data(), so it is not copied into the
232
+ // server rendered payload once per module.
233
+ this.wrapperProps = wrapperData.props || {}
171
234
 
172
235
  // Build module settings.
173
236
  component.settings = wrapperData.druxt || {}
@@ -206,7 +269,20 @@ export default {
206
269
  * @param {object} vm.$route - The current route.
207
270
  * @return {string} The current language code.
208
271
  */
209
- lang: ({ langcode, $route }) => langcode || ($route.meta || {}).langcode
272
+ lang: ({ langcode, $route }) => langcode || ($route.meta || {}).langcode,
273
+
274
+ /**
275
+ * The module's own propsData, tracked so a prop change rebuilds it.
276
+ *
277
+ * Reading it here registers whatever the module's `propsData()` callback
278
+ * reads, so each module declares its own dependencies by using them.
279
+ *
280
+ * @type {object}
281
+ */
282
+ modulePropsData() {
283
+ if (!(this.$options.druxt || {}).propsData) return null
284
+ return { langcode: this.lang, ...this.$options.druxt.propsData.call(this, this) }
285
+ }
210
286
  },
211
287
 
212
288
  watch: {
@@ -216,6 +292,27 @@ export default {
216
292
  }
217
293
  },
218
294
 
295
+ // Nuxt 2 doesn't re-run fetch() when a component's own props change, so a
296
+ // reused instance would keep the propsData its first fetch() built. Rebuild
297
+ // it in place instead of refetching: the data is unchanged, the props are not.
298
+ modulePropsData: {
299
+ deep: true,
300
+ handler(to) {
301
+ // An errored module renders DruxtDebug with its own props; leave them.
302
+ if (!to || this.component.is === 'DruxtDebug' || !Object.keys(this.component.propsData || {}).length) {
303
+ return
304
+ }
305
+ if (!hasChanged(this.component.propsData, to)) {
306
+ return
307
+ }
308
+
309
+ // After hydration this instance never ran fetch(), so read the resolved
310
+ // wrapper's props instead of the ones that fetch() put on the instance.
311
+ const resolved = ((this.$options.components || {})[this.component.is] || {}).options || {}
312
+ this.component = { ...this.component, ...this.getModulePropsData(this.wrapperProps || resolved.props || {}) }
313
+ }
314
+ },
315
+
219
316
  model() {
220
317
  if (this.component.props && this.component.props.value !== this.model) {
221
318
  this.component.props.value = this.model
package/dist/druxt.esm.js CHANGED
@@ -263,7 +263,7 @@ class DruxtClient {
263
263
  }
264
264
 
265
265
  var name = "druxt";
266
- var version = "0.24.1-dev.20260920213131";
266
+ var version = "0.24.1-dev.20260921024946";
267
267
  var description = "The Fully Decoupled Drupal Framework for Nuxt.js.";
268
268
  var keywords = [
269
269
  "cms",
package/dist/druxt.ssr.js CHANGED
@@ -276,7 +276,7 @@ class DruxtClient {
276
276
  }
277
277
 
278
278
  var name = "druxt";
279
- var version = "0.24.1-dev.20260920213131";
279
+ var version = "0.24.1-dev.20260921024946";
280
280
  var description = "The Fully Decoupled Drupal Framework for Nuxt.js.";
281
281
  var keywords = [
282
282
  "cms",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "druxt",
3
- "version": "0.24.1-dev.20260920213131",
3
+ "version": "0.24.1-dev.20260921024946",
4
4
  "description": "The Fully Decoupled Drupal Framework for Nuxt.js.",
5
5
  "keywords": [
6
6
  "cms",