druxt 0.15.0 → 0.16.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # druxt
2
2
 
3
+ ## 0.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - dc226c2: Made the \$druxt plugin first to be available to all Druxt module plugins.
8
+ - 7b749bd: Added improved error handling.
9
+
3
10
  ## 0.15.0
4
11
 
5
12
  ### Minor Changes
@@ -107,12 +107,16 @@ export default {
107
107
 
108
108
  // Fetch configuration.
109
109
  if ((this.$options.druxt || {}).fetchConfig) {
110
- await this.$options.druxt.fetchConfig.call(this)
110
+ try {
111
+ await this.$options.druxt.fetchConfig.call(this)
112
+ } catch(err) {
113
+ return this.error(err)
114
+ }
111
115
  }
112
116
 
113
117
  // Build wrapper component object.
114
118
  let options = []
115
- const hasDefaultTemplate = !!(this.$vnode.data.scopedSlots || {}).default
119
+ const hasDefaultTemplate = !!(((this.$vnode || {}).data || {}).scopedSlots || {}).default
116
120
  // Load wrapper components if:
117
121
  if (
118
122
  // No default template and wrapper isn't false OR
@@ -138,7 +142,11 @@ export default {
138
142
 
139
143
  // Fetch resource.
140
144
  if ((this.$options.druxt || {}).fetchData) {
141
- await this.$options.druxt.fetchData.call(this, component.settings)
145
+ try {
146
+ await this.$options.druxt.fetchData.call(this, component.settings)
147
+ } catch(err) {
148
+ return this.error(err, { component })
149
+ }
142
150
  }
143
151
 
144
152
  // Get scoped slots.
@@ -153,7 +161,7 @@ export default {
153
161
 
154
162
  watch: {
155
163
  model() {
156
- if (this.component.props.value !== this.model) {
164
+ if (this.component.props && this.component.props.value !== this.model) {
157
165
  this.component.props.value = this.model
158
166
 
159
167
  // Only emit 'input' if using the default 'DruxtWrapper' component.
@@ -172,6 +180,31 @@ export default {
172
180
 
173
181
  /** */
174
182
  methods: {
183
+ /**
184
+ * Sets the component to render a DruxtDebug error message.
185
+ */
186
+ error(err, context = {}) {
187
+ // Build error details.
188
+ const { url } = err.druxt || {}
189
+ const title = (err.response || {}).statusText || ((((err.response || {}).data || {}).errors || [])[0] || {}).title
190
+ const summary = (err.response || {}).status
191
+ ? [(err.response || {}).status, title].filter((s) => s).join(': ')
192
+ : err.message
193
+
194
+ // Set the component to a Debug component with error details.
195
+ this.component = {
196
+ ...context.component || {},
197
+ is: 'DruxtDebug',
198
+ props: {
199
+ json: {
200
+ url,
201
+ errors: ((err.response || {}).data || {}).errors
202
+ },
203
+ summary
204
+ }
205
+ }
206
+ },
207
+
175
208
  /**
176
209
  * Get list of module wrapper components.
177
210
  *
package/dist/druxt.esm.js CHANGED
@@ -9,7 +9,7 @@ import Vue from 'vue';
9
9
  import { DrupalJsonApiParams } from 'drupal-jsonapi-params';
10
10
 
11
11
  var name = "druxt";
12
- var version = "0.15.0";
12
+ var version = "0.16.0";
13
13
  var description = "The Fully Decoupled Drupal Framework for Nuxt.js.";
14
14
  var keywords = [
15
15
  "cms",
@@ -127,6 +127,15 @@ const DruxtNuxtModule = function(moduleOptions = {}) {
127
127
  fileName: "druxt.js",
128
128
  options
129
129
  });
130
+ const extendPlugins = this.options.extendPlugins;
131
+ this.options.extendPlugins = (plugins) => {
132
+ plugins = typeof extendPlugins === "function" ? extendPlugins(plugins) : plugins;
133
+ const index = plugins.findIndex(({ src }) => src === `${this.options.buildDir}/druxt.js`);
134
+ const plugin = plugins[index];
135
+ plugins.splice(index, 1);
136
+ plugins.unshift(plugin);
137
+ return plugins;
138
+ };
130
139
  this.addPlugin({
131
140
  src: resolve(__dirname, "../templates/store.js"),
132
141
  fileName: "store/druxt.js",
@@ -137,7 +146,6 @@ const DruxtNuxtModule = function(moduleOptions = {}) {
137
146
  this.options.cli.badgeMessages.push(`${chalk.blue.bold("Druxt")} @ v${meta.version}`);
138
147
  this.options.cli.badgeMessages.push(`${chalk.bold("API:")} ${chalk.blue.underline(options.baseUrl + options.endpoint)}`);
139
148
  };
140
- DruxtNuxtModule.meta = meta;
141
149
 
142
150
  class DruxtClient {
143
151
  constructor(baseUrl, options = {}) {
@@ -206,9 +214,16 @@ class DruxtClient {
206
214
  }
207
215
  }
208
216
  if (Object.keys(permissions).length) {
209
- throw new TypeError(`${res.data.meta.omitted.detail}
210
-
211
- Required permissions: ${Object.keys(permissions).join(", ")}.`);
217
+ const err = {
218
+ response: {
219
+ statusText: res.data.meta.omitted.detail,
220
+ data: {
221
+ errors: [{ detail: `Required permissions:
222
+ - ${Object.keys(permissions).join("\n - ")}` }]
223
+ }
224
+ }
225
+ };
226
+ throw err;
212
227
  }
213
228
  }
214
229
  }
@@ -228,19 +243,49 @@ class DruxtClient {
228
243
  }
229
244
  });
230
245
  } catch (err) {
231
- response = (err.response || {}).data || err.message;
246
+ this.error(err, { url: href });
232
247
  }
233
248
  return response;
234
249
  }
250
+ error(err, context = {}) {
251
+ let { url } = context;
252
+ if (!url && ((err.response || {}).config || {}).url) {
253
+ url = err.response.config.url;
254
+ }
255
+ const title = [
256
+ (err.response || {}).status,
257
+ (err.response || {}).statusText || err.message
258
+ ].filter((s) => s).join(": ");
259
+ const meta = { url: url && [this.options.baseUrl, url].join("") };
260
+ let message = [title];
261
+ if (Object.values(meta).filter((o) => o).length) {
262
+ message.push(Object.entries(meta).filter(([, v]) => v).map(([key, value]) => `${key.toUpperCase()}: ${value}`).join("\n"));
263
+ }
264
+ if (((((err.response || {}).data || {}).errors || [])[0] || {}).detail) {
265
+ message.push(err.response.data.errors[0].detail);
266
+ }
267
+ const error = Error(message.join("\n\n"));
268
+ error.response = err.response;
269
+ error.druxt = context;
270
+ throw error;
271
+ }
272
+ async get(url, options) {
273
+ try {
274
+ const res = await this.axios.get(url, options);
275
+ this.checkPermissions(res);
276
+ return res;
277
+ } catch (err) {
278
+ this.error(err, { url });
279
+ }
280
+ }
235
281
  async getCollection(type, query) {
236
282
  const { href } = await this.getIndex(type);
237
283
  if (!href) {
238
284
  return false;
239
285
  }
240
286
  const url = this.buildQueryUrl(href, query);
241
- const res = await this.axios.get(url);
242
- this.checkPermissions(res);
243
- return res.data;
287
+ const { data } = await this.get(url);
288
+ return data;
244
289
  }
245
290
  async getCollectionAll(type, query) {
246
291
  const collections = [];
@@ -260,16 +305,27 @@ class DruxtClient {
260
305
  if (this.index && resource) {
261
306
  return this.index[resource] ? this.index[resource] : false;
262
307
  }
263
- let index = ((await this.axios.get(this.options.endpoint) || {}).data || {}).links;
308
+ const url = this.options.endpoint;
309
+ const { data } = await this.get(url);
310
+ let index = data.links;
311
+ if (typeof index !== "object") {
312
+ const err = { response: { statusText: "Invalid JSON:API endpoint" } };
313
+ this.error(err, { url });
314
+ }
264
315
  const baseUrl = this.options.baseUrl;
265
316
  index = Object.fromEntries(Object.entries(index).map(([key, value]) => {
266
317
  value.href = value.href.replace(baseUrl, "");
267
318
  return [key, value];
268
319
  }));
269
320
  if (index[this.options.jsonapiResourceConfig]) {
270
- const resources = await this.axios.get(index[this.options.jsonapiResourceConfig].href);
271
- for (const resourceType in resources.data.data) {
272
- const resource2 = resources.data.data[resourceType];
321
+ let resources = [];
322
+ try {
323
+ resources = (await this.get(index[this.options.jsonapiResourceConfig].href)).data.data;
324
+ } catch (err) {
325
+ this.log.warn(err.message);
326
+ }
327
+ for (const resourceType in resources) {
328
+ const resource2 = resources[resourceType];
273
329
  const internal = resource2.attributes.drupal_internal__id.split("--");
274
330
  const item = {
275
331
  resourceType: resource2.attributes.resourceType,
@@ -285,11 +341,7 @@ class DruxtClient {
285
341
  }
286
342
  }
287
343
  this.index = index;
288
- if (resource) {
289
- const response = this.index[resource] ? this.index[resource] : false;
290
- return response;
291
- }
292
- return this.index;
344
+ return resource ? this.index[resource] || false : this.index;
293
345
  }
294
346
  async getRelated(type, id, related, query) {
295
347
  if (!id || !type || !related) {
@@ -300,12 +352,8 @@ class DruxtClient {
300
352
  href = this.options.endpoint + "/" + type.replace("--", "/");
301
353
  }
302
354
  const url = this.buildQueryUrl(`${href}/${id}/${related}`, query);
303
- try {
304
- const related2 = await this.axios.get(url);
305
- return related2.data;
306
- } catch (e) {
307
- return false;
308
- }
355
+ const { data } = await this.get(url);
356
+ return data;
309
357
  }
310
358
  async getResource(type, id, query) {
311
359
  if (!id || !type) {
@@ -315,13 +363,9 @@ class DruxtClient {
315
363
  if (!href) {
316
364
  href = this.options.endpoint + "/" + type.replace("--", "/");
317
365
  }
318
- const url = this.buildQueryUrl(`${href}/${id}`, query);
319
- try {
320
- const resource = await this.axios.get(url);
321
- return resource.data;
322
- } catch (e) {
323
- return false;
324
- }
366
+ const url = this.buildQueryUrl([href, id].join("/"), query);
367
+ const { data } = await this.get(url);
368
+ return data;
325
369
  }
326
370
  async updateResource(resource) {
327
371
  const { href } = await this.getIndex(resource.type);
@@ -337,7 +381,7 @@ class DruxtClient {
337
381
  }
338
382
  });
339
383
  } catch (err) {
340
- response = (err.response || {}).data || err.message;
384
+ this.error(err);
341
385
  }
342
386
  return response;
343
387
  }
@@ -348,7 +392,7 @@ const getDrupalJsonApiParams = (query) => {
348
392
  };
349
393
 
350
394
  const dehydrateResources = ({ commit, queryObject, resources }) => {
351
- return resources.map((data) => {
395
+ return (resources || []).map((data) => {
352
396
  const link = decodeURI(((data.links || {}).self || {}).href || "");
353
397
  const href = typeof (queryObject.fields || {})[data.type] === "string" ? [link.split("?")[0], `fields[${data.type}]=${queryObject.fields[data.type]}`].join("?") : link;
354
398
  commit("druxt/addResource", {
@@ -539,5 +583,7 @@ class DruxtClass {
539
583
  }
540
584
  }
541
585
 
586
+ DruxtNuxtModule.meta = require("../package.json");
587
+
542
588
  export default DruxtNuxtModule;
543
589
  export { DruxtClass, DruxtClient, DruxtStore };
package/dist/druxt.ssr.js CHANGED
@@ -22,7 +22,7 @@ const md5__default = /*#__PURE__*/_interopDefaultLegacy(md5);
22
22
  const Vue__default = /*#__PURE__*/_interopDefaultLegacy(Vue);
23
23
 
24
24
  var name = "druxt";
25
- var version = "0.15.0";
25
+ var version = "0.16.0";
26
26
  var description = "The Fully Decoupled Drupal Framework for Nuxt.js.";
27
27
  var keywords = [
28
28
  "cms",
@@ -140,6 +140,15 @@ const DruxtNuxtModule = function(moduleOptions = {}) {
140
140
  fileName: "druxt.js",
141
141
  options
142
142
  });
143
+ const extendPlugins = this.options.extendPlugins;
144
+ this.options.extendPlugins = (plugins) => {
145
+ plugins = typeof extendPlugins === "function" ? extendPlugins(plugins) : plugins;
146
+ const index = plugins.findIndex(({ src }) => src === `${this.options.buildDir}/druxt.js`);
147
+ const plugin = plugins[index];
148
+ plugins.splice(index, 1);
149
+ plugins.unshift(plugin);
150
+ return plugins;
151
+ };
143
152
  this.addPlugin({
144
153
  src: path.resolve(__dirname, "../templates/store.js"),
145
154
  fileName: "store/druxt.js",
@@ -150,7 +159,6 @@ const DruxtNuxtModule = function(moduleOptions = {}) {
150
159
  this.options.cli.badgeMessages.push(`${chalk__default['default'].blue.bold("Druxt")} @ v${meta.version}`);
151
160
  this.options.cli.badgeMessages.push(`${chalk__default['default'].bold("API:")} ${chalk__default['default'].blue.underline(options.baseUrl + options.endpoint)}`);
152
161
  };
153
- DruxtNuxtModule.meta = meta;
154
162
 
155
163
  class DruxtClient {
156
164
  constructor(baseUrl, options = {}) {
@@ -219,9 +227,16 @@ class DruxtClient {
219
227
  }
220
228
  }
221
229
  if (Object.keys(permissions).length) {
222
- throw new TypeError(`${res.data.meta.omitted.detail}
223
-
224
- Required permissions: ${Object.keys(permissions).join(", ")}.`);
230
+ const err = {
231
+ response: {
232
+ statusText: res.data.meta.omitted.detail,
233
+ data: {
234
+ errors: [{ detail: `Required permissions:
235
+ - ${Object.keys(permissions).join("\n - ")}` }]
236
+ }
237
+ }
238
+ };
239
+ throw err;
225
240
  }
226
241
  }
227
242
  }
@@ -241,19 +256,49 @@ class DruxtClient {
241
256
  }
242
257
  });
243
258
  } catch (err) {
244
- response = (err.response || {}).data || err.message;
259
+ this.error(err, { url: href });
245
260
  }
246
261
  return response;
247
262
  }
263
+ error(err, context = {}) {
264
+ let { url } = context;
265
+ if (!url && ((err.response || {}).config || {}).url) {
266
+ url = err.response.config.url;
267
+ }
268
+ const title = [
269
+ (err.response || {}).status,
270
+ (err.response || {}).statusText || err.message
271
+ ].filter((s) => s).join(": ");
272
+ const meta = { url: url && [this.options.baseUrl, url].join("") };
273
+ let message = [title];
274
+ if (Object.values(meta).filter((o) => o).length) {
275
+ message.push(Object.entries(meta).filter(([, v]) => v).map(([key, value]) => `${key.toUpperCase()}: ${value}`).join("\n"));
276
+ }
277
+ if (((((err.response || {}).data || {}).errors || [])[0] || {}).detail) {
278
+ message.push(err.response.data.errors[0].detail);
279
+ }
280
+ const error = Error(message.join("\n\n"));
281
+ error.response = err.response;
282
+ error.druxt = context;
283
+ throw error;
284
+ }
285
+ async get(url, options) {
286
+ try {
287
+ const res = await this.axios.get(url, options);
288
+ this.checkPermissions(res);
289
+ return res;
290
+ } catch (err) {
291
+ this.error(err, { url });
292
+ }
293
+ }
248
294
  async getCollection(type, query) {
249
295
  const { href } = await this.getIndex(type);
250
296
  if (!href) {
251
297
  return false;
252
298
  }
253
299
  const url = this.buildQueryUrl(href, query);
254
- const res = await this.axios.get(url);
255
- this.checkPermissions(res);
256
- return res.data;
300
+ const { data } = await this.get(url);
301
+ return data;
257
302
  }
258
303
  async getCollectionAll(type, query) {
259
304
  const collections = [];
@@ -273,16 +318,27 @@ class DruxtClient {
273
318
  if (this.index && resource) {
274
319
  return this.index[resource] ? this.index[resource] : false;
275
320
  }
276
- let index = ((await this.axios.get(this.options.endpoint) || {}).data || {}).links;
321
+ const url = this.options.endpoint;
322
+ const { data } = await this.get(url);
323
+ let index = data.links;
324
+ if (typeof index !== "object") {
325
+ const err = { response: { statusText: "Invalid JSON:API endpoint" } };
326
+ this.error(err, { url });
327
+ }
277
328
  const baseUrl = this.options.baseUrl;
278
329
  index = Object.fromEntries(Object.entries(index).map(([key, value]) => {
279
330
  value.href = value.href.replace(baseUrl, "");
280
331
  return [key, value];
281
332
  }));
282
333
  if (index[this.options.jsonapiResourceConfig]) {
283
- const resources = await this.axios.get(index[this.options.jsonapiResourceConfig].href);
284
- for (const resourceType in resources.data.data) {
285
- const resource2 = resources.data.data[resourceType];
334
+ let resources = [];
335
+ try {
336
+ resources = (await this.get(index[this.options.jsonapiResourceConfig].href)).data.data;
337
+ } catch (err) {
338
+ this.log.warn(err.message);
339
+ }
340
+ for (const resourceType in resources) {
341
+ const resource2 = resources[resourceType];
286
342
  const internal = resource2.attributes.drupal_internal__id.split("--");
287
343
  const item = {
288
344
  resourceType: resource2.attributes.resourceType,
@@ -298,11 +354,7 @@ class DruxtClient {
298
354
  }
299
355
  }
300
356
  this.index = index;
301
- if (resource) {
302
- const response = this.index[resource] ? this.index[resource] : false;
303
- return response;
304
- }
305
- return this.index;
357
+ return resource ? this.index[resource] || false : this.index;
306
358
  }
307
359
  async getRelated(type, id, related, query) {
308
360
  if (!id || !type || !related) {
@@ -313,12 +365,8 @@ class DruxtClient {
313
365
  href = this.options.endpoint + "/" + type.replace("--", "/");
314
366
  }
315
367
  const url = this.buildQueryUrl(`${href}/${id}/${related}`, query);
316
- try {
317
- const related2 = await this.axios.get(url);
318
- return related2.data;
319
- } catch (e) {
320
- return false;
321
- }
368
+ const { data } = await this.get(url);
369
+ return data;
322
370
  }
323
371
  async getResource(type, id, query) {
324
372
  if (!id || !type) {
@@ -328,13 +376,9 @@ class DruxtClient {
328
376
  if (!href) {
329
377
  href = this.options.endpoint + "/" + type.replace("--", "/");
330
378
  }
331
- const url = this.buildQueryUrl(`${href}/${id}`, query);
332
- try {
333
- const resource = await this.axios.get(url);
334
- return resource.data;
335
- } catch (e) {
336
- return false;
337
- }
379
+ const url = this.buildQueryUrl([href, id].join("/"), query);
380
+ const { data } = await this.get(url);
381
+ return data;
338
382
  }
339
383
  async updateResource(resource) {
340
384
  const { href } = await this.getIndex(resource.type);
@@ -350,7 +394,7 @@ class DruxtClient {
350
394
  }
351
395
  });
352
396
  } catch (err) {
353
- response = (err.response || {}).data || err.message;
397
+ this.error(err);
354
398
  }
355
399
  return response;
356
400
  }
@@ -361,7 +405,7 @@ const getDrupalJsonApiParams = (query) => {
361
405
  };
362
406
 
363
407
  const dehydrateResources = ({ commit, queryObject, resources }) => {
364
- return resources.map((data) => {
408
+ return (resources || []).map((data) => {
365
409
  const link = decodeURI(((data.links || {}).self || {}).href || "");
366
410
  const href = typeof (queryObject.fields || {})[data.type] === "string" ? [link.split("?")[0], `fields[${data.type}]=${queryObject.fields[data.type]}`].join("?") : link;
367
411
  commit("druxt/addResource", {
@@ -552,6 +596,8 @@ class DruxtClass {
552
596
  }
553
597
  }
554
598
 
599
+ DruxtNuxtModule.meta = require("../package.json");
600
+
555
601
  exports.DruxtClass = DruxtClass;
556
602
  exports.DruxtClient = DruxtClient;
557
603
  exports.DruxtStore = DruxtStore;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "druxt",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "The Fully Decoupled Drupal Framework for Nuxt.js.",
5
5
  "keywords": [
6
6
  "cms",