Comparing version 0.8.3 to 0.9.0
import { resolve } from 'path'; | ||
import axios from 'axios'; | ||
import { stringify } from 'querystring'; | ||
import { pascalCase, splitByCase } from 'scule'; | ||
import Vue from 'vue'; | ||
import merge from 'deepmerge'; | ||
import md5 from 'md5'; | ||
import Vue from 'vue'; | ||
import { DrupalJsonApiParams } from 'drupal-jsonapi-params'; | ||
/** | ||
* The Vue.js Druxt component. | ||
* | ||
* @example @lang vue | ||
* <Druxt | ||
* :module="module" | ||
* :props-data="propsData" | ||
* :wrapper="{ | ||
* component, | ||
* propsData: {} | ||
* }" | ||
* /> | ||
*/ | ||
var script = { | ||
name: 'Druxt', | ||
/** | ||
* Vue.js Props. | ||
*/ | ||
props: { | ||
/** | ||
* Inner element. | ||
* | ||
* @type {object} | ||
* @default { component: 'div', propsData: {} } | ||
*/ | ||
inner: { | ||
type: [Object, Boolean], | ||
default: function () { return ({ | ||
component: 'div', | ||
propsData: {}, | ||
}); } | ||
}, | ||
/** | ||
* The DruxtJS module to render. | ||
* | ||
* @type {string} | ||
* | ||
* @example @lang vue <caption>Using the [DruxtJS Site module](https://site.druxtjs.org).</caption> | ||
* <Druxt module="site" /> | ||
*/ | ||
module: { | ||
type: String, | ||
required: true, | ||
}, | ||
/** | ||
* Props data to bind to the specified DruxtJS module. | ||
* | ||
* @type {object} | ||
* | ||
* @example @lang vue <caption>Using the [DruxtJS Entity module](https://entity.druxtjs.org) to render a 'node--article' resource.</caption> | ||
* <Druxt | ||
* module="entity" | ||
* :props-data="{ | ||
* mode: 'teaser', | ||
* type: 'node--article', | ||
* uuid | ||
* }" | ||
* /> | ||
*/ | ||
propsData: { | ||
type: Object, | ||
default: function () { return ({}); } | ||
}, | ||
/** | ||
* The module value. | ||
* | ||
* @type {(Array|Boolean|Date|Number|Object|String)} | ||
* @model | ||
*/ | ||
value: { | ||
type: [Array, Boolean, Date, Number, Object, String], | ||
default: null, | ||
}, | ||
/** | ||
* Wrapper element. | ||
* | ||
* @type {object} | ||
* @default { component: 'div', propsData: {} } | ||
*/ | ||
wrapper: { | ||
type: [Object, Boolean], | ||
default: function () { return ({ | ||
component: 'div', | ||
propsData: {}, | ||
}); } | ||
}, | ||
}, | ||
/** | ||
* Vue.js Data object. | ||
* | ||
* @property {objects} components - The module and wrapper components settinsg. | ||
* @property {object} model - The model object. | ||
*/ | ||
data: function (ref) { | ||
var value = ref.value; | ||
return ({ | ||
component: { | ||
is: undefined, | ||
propsData: {}, | ||
}, | ||
model: value, | ||
const DruxtNuxtModule = function(moduleOptions = {}) { | ||
const options = { | ||
...moduleOptions, | ||
...(this.options || {}).druxt | ||
}; | ||
this.addPlugin({ | ||
src: resolve(__dirname, "../nuxt/plugin.js"), | ||
fileName: "druxt.js", | ||
options | ||
}); | ||
}, | ||
this.addPlugin({ | ||
src: resolve(__dirname, "../nuxt/store.js"), | ||
fileName: "store/druxt.js", | ||
options | ||
}); | ||
this.options.store = true; | ||
}; | ||
DruxtNuxtModule.meta = require("../package.json"); | ||
created: function created() { | ||
this.setModuleComponent(); | ||
}, | ||
methods: { | ||
/** | ||
* Sets the module component and propsData. | ||
*/ | ||
setModuleComponent: function setModuleComponent() { | ||
var component = "Druxt" + (this.module.split('-').map(function (string) { return string.charAt(0).toUpperCase() + string.slice(1); }).join('')); | ||
if (!this.$options.components[component]) { | ||
return | ||
} | ||
// Set component data. | ||
this.component.is = component; | ||
this.component.propsData = this.propsData; | ||
}, | ||
}, | ||
watch: { | ||
model: function model() { | ||
if (this.value !== this.model) { | ||
this.$emit('input', this.model); | ||
} | ||
}, | ||
value: function value() { | ||
if (this.value !== this.model) { | ||
this.model = this.value; | ||
} | ||
class DruxtClient { | ||
constructor(baseUrl, options = {}) { | ||
if (!baseUrl) { | ||
throw new Error("The 'baseUrl' parameter is required."); | ||
} | ||
}, | ||
render: function render(h) { | ||
var component = h(this.component.is, { | ||
props: Object.assign({}, {wrapper: this.inner}, | ||
this.component.propsData, | ||
this.$attrs, | ||
{value: this.model}), | ||
ref: 'module', | ||
}); | ||
if ((this.wrapper || {}).component) { | ||
return h(this.wrapper.component, { props: this.wrapper.propsData }, [component]) | ||
let axiosSettings = { baseURL: baseUrl }; | ||
if (typeof options.axios === "object") { | ||
axiosSettings = Object.assign(axiosSettings, options.axios); | ||
delete options.axios; | ||
} | ||
return component | ||
this.axios = axios.create(axiosSettings); | ||
this.options = { | ||
endpoint: "/jsonapi", | ||
jsonapiResourceConfig: "jsonapi_resource_config--jsonapi_resource_config", | ||
...options | ||
}; | ||
} | ||
}; | ||
function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { | ||
if (typeof shadowMode !== 'boolean') { | ||
createInjectorSSR = createInjector; | ||
createInjector = shadowMode; | ||
shadowMode = false; | ||
addHeaders(headers) { | ||
if (typeof headers === "undefined") { | ||
return false; | ||
} | ||
// Vue.extend constructor export interop. | ||
var options = typeof script === 'function' ? script.options : script; | ||
// render functions | ||
if (template && template.render) { | ||
options.render = template.render; | ||
options.staticRenderFns = template.staticRenderFns; | ||
options._compiled = true; | ||
// functional template | ||
if (isFunctionalTemplate) { | ||
options.functional = true; | ||
} | ||
for (const name in headers) { | ||
this.axios.defaults.headers.common[name] = headers[name]; | ||
} | ||
// scopedId | ||
if (scopeId) { | ||
options._scopeId = scopeId; | ||
} | ||
buildQueryUrl(url, query) { | ||
if (!query) { | ||
return url; | ||
} | ||
var hook; | ||
if (moduleIdentifier) { | ||
// server build | ||
hook = function (context) { | ||
// 2.3 injection | ||
context = | ||
context || // cached call | ||
(this.$vnode && this.$vnode.ssrContext) || // stateful | ||
(this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional | ||
// 2.2 with runInNewContext: true | ||
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { | ||
context = __VUE_SSR_CONTEXT__; | ||
} | ||
// inject component styles | ||
if (style) { | ||
style.call(this, createInjectorSSR(context)); | ||
} | ||
// register component module identifier for async chunk inference | ||
if (context && context._registeredComponents) { | ||
context._registeredComponents.add(moduleIdentifier); | ||
} | ||
}; | ||
// used by ssr in case component is cached and beforeCreate | ||
// never gets called | ||
options._ssrRegister = hook; | ||
if (typeof query === "string") { | ||
return query.charAt(0) === "?" ? url + query : [url, query].join("?"); | ||
} | ||
else if (style) { | ||
hook = shadowMode | ||
? function (context) { | ||
style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot)); | ||
} | ||
: function (context) { | ||
style.call(this, createInjector(context)); | ||
}; | ||
if (typeof query === "object" && typeof query.getQueryString === "function" && (query = query.getQueryString())) { | ||
return [url, query].join("?"); | ||
} | ||
if (hook) { | ||
if (options.functional) { | ||
// register for functional component in vue file | ||
var originalRender = options.render; | ||
options.render = function renderWithStyleInjection(h, context) { | ||
hook.call(context); | ||
return originalRender(h, context); | ||
}; | ||
} | ||
else { | ||
// inject component registration as beforeCreate hook | ||
var existing = options.beforeCreate; | ||
options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; | ||
} | ||
if (typeof query === "object" && Object.keys(query).length) { | ||
return [url, stringify(query)].join("?"); | ||
} | ||
return script; | ||
} | ||
/* script */ | ||
var __vue_script__ = script; | ||
/* template */ | ||
/* style */ | ||
var __vue_inject_styles__ = undefined; | ||
/* scoped */ | ||
var __vue_scope_id__ = undefined; | ||
/* module identifier */ | ||
var __vue_module_identifier__ = undefined; | ||
/* functional template */ | ||
var __vue_is_functional_template__ = undefined; | ||
/* style inject */ | ||
/* style inject SSR */ | ||
/* style inject shadow dom */ | ||
var __vue_component__ = /*#__PURE__*/normalizeComponent( | ||
{}, | ||
__vue_inject_styles__, | ||
__vue_script__, | ||
__vue_scope_id__, | ||
__vue_is_functional_template__, | ||
__vue_module_identifier__, | ||
false, | ||
undefined, | ||
undefined, | ||
undefined | ||
); | ||
// | ||
// | ||
// | ||
// | ||
// | ||
// | ||
/** | ||
* The default Druxt module wrapper Vue.js component. | ||
* | ||
* This component is used by the Druxt component if an appropriate component can not be found | ||
* for the specified module. | ||
* | ||
* @private | ||
*/ | ||
var script$1 = { | ||
name: 'DruxtWrapper' | ||
}; | ||
/* script */ | ||
var __vue_script__$1 = script$1; | ||
/* template */ | ||
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_vm._t("default")],2)}; | ||
var __vue_staticRenderFns__ = []; | ||
/* style */ | ||
var __vue_inject_styles__$1 = undefined; | ||
/* scoped */ | ||
var __vue_scope_id__$1 = undefined; | ||
/* module identifier */ | ||
var __vue_module_identifier__$1 = undefined; | ||
/* functional template */ | ||
var __vue_is_functional_template__$1 = false; | ||
/* style inject */ | ||
/* style inject SSR */ | ||
/* style inject shadow dom */ | ||
var __vue_component__$1 = /*#__PURE__*/normalizeComponent( | ||
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, | ||
__vue_inject_styles__$1, | ||
__vue_script__$1, | ||
__vue_scope_id__$1, | ||
__vue_is_functional_template__$1, | ||
__vue_module_identifier__$1, | ||
false, | ||
undefined, | ||
undefined, | ||
undefined | ||
); | ||
/** | ||
* Nuxt module function to install Druxt. | ||
* | ||
* @param {ModuleOptions} moduleOptions - DruxtJS module options. | ||
* | ||
* @example <caption>Nuxt configuration with module options</caption> @lang js | ||
* module.exports = { | ||
* modules: [ | ||
* ['druxt', { baseUrl: 'https://demo-api.druxtjs.org' }] | ||
* ] | ||
* } | ||
* | ||
* @example <caption>Nuxt configuration with root level options</caption> @lang js | ||
* module.exports = { | ||
* modules: [ | ||
* 'druxt' | ||
* ], | ||
* druxt: { | ||
* baseUrl: 'https://demo-api.druxtjs.org' | ||
* } | ||
* } | ||
*/ | ||
var DruxtNuxtModule = function (moduleOptions) { | ||
if ( moduleOptions === void 0 ) moduleOptions = {}; | ||
var options = Object.assign({}, moduleOptions, | ||
(this.options || {}).druxt); | ||
// Add plugin. | ||
this.addPlugin({ | ||
src: resolve(__dirname, '../nuxt/plugin.js'), | ||
fileName: 'druxt.js', | ||
options: options | ||
}); | ||
// Add Vuex plugin. | ||
this.addPlugin({ | ||
src: resolve(__dirname, '../nuxt/store.js'), | ||
fileName: 'store/druxt.js', | ||
options: options | ||
}); | ||
// Enable Vuex Store. | ||
this.options.store = true; | ||
}; | ||
DruxtNuxtModule.meta = require('../package.json'); | ||
/** | ||
* Module options object. | ||
* | ||
* @typedef {object} ModuleOptions | ||
* @property {string} baseUrl - The Base URL of the Drupal JSON:API backend. | ||
*/ | ||
/** | ||
* Druxt JSON:API client. | ||
*/ | ||
var DruxtClient = function DruxtClient(baseUrl, options) { | ||
if ( options === void 0 ) options = {}; | ||
// Check for URL. | ||
if (!baseUrl) { | ||
throw new Error('The \'baseUrl\' parameter is required.') | ||
return url; | ||
} | ||
checkPermissions(res) { | ||
if (res.data.meta && res.data.meta.omitted) { | ||
const permissions = {}; | ||
delete res.data.meta.omitted.links.help; | ||
for (const key in res.data.meta.omitted.links) { | ||
const link = res.data.meta.omitted.links[key]; | ||
const match = link.meta.detail.match(/'(.*?)'/); | ||
if (match && match[1]) { | ||
permissions[match[1]] = true; | ||
} | ||
} | ||
if (Object.keys(permissions).length) { | ||
throw new TypeError(`${res.data.meta.omitted.detail} | ||
// Setup Axios. | ||
var axiosSettings = { baseURL: baseUrl }; | ||
if (typeof options.axios === 'object') { | ||
axiosSettings = Object.assign(axiosSettings, options.axios); | ||
delete options.axios; | ||
} | ||
/** | ||
* The Axios instance. | ||
* | ||
* @see {@link https://github.com/axios/axios#instance-methods} | ||
* @type {object} | ||
*/ | ||
this.axios = axios.create(axiosSettings); | ||
/** | ||
* Druxt base options. | ||
* @type {object} | ||
* @private | ||
*/ | ||
this.options = Object.assign({}, {endpoint: '/jsonapi', | ||
jsonapiResourceConfig: 'jsonapi_resource_config--jsonapi_resource_config'}, | ||
options); | ||
}; | ||
/** | ||
* Add headers to the Axios instance. | ||
* | ||
* @example @lang js | ||
* this.$druxt.addHeaders({ 'Authorization': `Basic ${token}` }) | ||
* | ||
* @param {object} headers - An object containing HTTP headers. | ||
*/ | ||
DruxtClient.prototype.addHeaders = function addHeaders (headers) { | ||
if (typeof headers === 'undefined') { | ||
return false | ||
} | ||
for (var name in headers) { | ||
this.axios.defaults.headers.common[name] = headers[name]; | ||
} | ||
}; | ||
/** | ||
* Build query URL. | ||
* | ||
* @example @lang js | ||
* const query = new DrupalJsonApiParams() | ||
* query.addFilter('status', '1') | ||
* const queryUrl = this.$druxt.buildQueryUrl(resourceUrl, query) | ||
* | ||
* @param {string} url - The base query URL. | ||
* @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. | ||
* | ||
* @return {string} The URL with query string. | ||
*/ | ||
DruxtClient.prototype.buildQueryUrl = function buildQueryUrl (url, query) { | ||
if (!query) { | ||
return url | ||
} | ||
// If Query is string... | ||
if (typeof query === 'string') { | ||
return query.charAt(0) === '?' ? url + query : [url, query].join('?') | ||
} | ||
// If Query is object with 'getQueryString' function, (e.g., drupal-jsonapi-params)... | ||
if (typeof query === 'object' && typeof query.getQueryString === 'function' && (query = query.getQueryString())) { | ||
return [url, query].join('?') | ||
} | ||
// If query is object... | ||
if (typeof query === 'object' && Object.keys(query).length) { | ||
return [url, stringify(query)].join('?') | ||
} | ||
// Else... | ||
return url | ||
}; | ||
/** | ||
* Check response for permissions. | ||
* | ||
* @todo - Move this to utils? | ||
* | ||
* @param {object} res - Axios GET request response object. | ||
* | ||
* @private | ||
*/ | ||
DruxtClient.prototype.checkPermissions = function checkPermissions (res) { | ||
// Error handling: Required permissions. | ||
if (res.data.meta && res.data.meta.omitted) { | ||
var permissions = {}; | ||
delete res.data.meta.omitted.links.help; | ||
for (var key in res.data.meta.omitted.links) { | ||
var link = res.data.meta.omitted.links[key]; | ||
var match = link.meta.detail.match(/'(.*?)'/); | ||
if (match && match[1]) { | ||
permissions[match[1]] = true; | ||
Required permissions: ${Object.keys(permissions).join(", ")}.`); | ||
} | ||
} | ||
if (Object.keys(permissions).length) { | ||
throw new TypeError(((res.data.meta.omitted.detail) + "\n\n Required permissions: " + (Object.keys(permissions).join(', ')) + ".")) | ||
} | ||
async getCollection(type, query) { | ||
const { href } = await this.getIndex(type); | ||
if (!href) { | ||
return false; | ||
} | ||
const url = this.buildQueryUrl(href, query); | ||
const res = await this.axios.get(url); | ||
this.checkPermissions(res); | ||
return res.data; | ||
} | ||
}; | ||
/** | ||
* Get a collection of resources from the JSON:API server. | ||
* | ||
* @param {string} type - The JSON:API Resource type. | ||
* @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. | ||
* | ||
* @returns {object} The JSON:API collection response. | ||
* | ||
* @example @lang js | ||
* const collection = await this.$druxt.getCollection('node--recipe') | ||
*/ | ||
DruxtClient.prototype.getCollection = async function getCollection (type, query) { | ||
var ref = await this.getIndex(type); | ||
var href = ref.href; | ||
if (!href) { | ||
return false | ||
} | ||
var url = this.buildQueryUrl(href, query); | ||
var res = await this.axios.get(url); | ||
this.checkPermissions(res); | ||
return res.data | ||
}; | ||
/** | ||
* Get all resources of a collection. | ||
* | ||
* @param {string} type - The JSON:API Resource type. | ||
* @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. | ||
* | ||
* @returns {object[]} An array of JSON:API collections. | ||
* | ||
* @example @lang js | ||
* const collections = await this.$druxt.getCollectionAll('node--recipe', 'fields[node--recipe]=title') | ||
*/ | ||
DruxtClient.prototype.getCollectionAll = async function getCollectionAll (type, query) { | ||
var collections = []; | ||
var res = await this.getCollection(type, query); | ||
collections.push(res); | ||
while (((res.links || {}).next || {}).href) { | ||
query = res.links.next.href.split('?')[1]; | ||
res = await this.getCollection(type, query); | ||
async getCollectionAll(type, query) { | ||
const collections = []; | ||
let res = await this.getCollection(type, query); | ||
collections.push(res); | ||
} | ||
return collections | ||
}; | ||
/** | ||
* Get index of all available resources, or the optionally specified resource. | ||
* | ||
* @example @lang js | ||
* const { href } = await this.$druxt.getIndex('node--article') | ||
* | ||
* @param {string} resource - (Optional) A specific resource to query. | ||
* | ||
* @returns {object} The resource index object or the specified resource. | ||
*/ | ||
DruxtClient.prototype.getIndex = async function getIndex (resource) { | ||
if (this.index && !resource) { | ||
return this.index | ||
} | ||
if (this.index && resource) { | ||
return this.index[resource] ? this.index[resource] : false | ||
} | ||
var index = await this.axios.get(this.options.endpoint); | ||
this.index = index.data.links; | ||
// Use JSON API resource config to decorate the index. | ||
if (this.index[this.options.jsonapiResourceConfig]) { | ||
var resources = await this.axios.get(this.index[this.options.jsonapiResourceConfig].href); | ||
for (var resourceType in resources.data.data) { | ||
var resource$1 = resources.data.data[resourceType]; | ||
var internal = resource$1.attributes.drupal_internal__id.split('--'); | ||
var item = { | ||
resourceType: resource$1.attributes.resourceType, | ||
entityType: internal[0], | ||
bundle: internal[1], | ||
resourceFields: resource$1.attributes.resourceFields | ||
}; | ||
var id = [item.entityType, item.bundle].join('--'); | ||
this.index[id] = Object.assign({}, item, | ||
this.index[id]); | ||
while (((res.links || {}).next || {}).href) { | ||
query = res.links.next.href.split("?")[1]; | ||
res = await this.getCollection(type, query); | ||
collections.push(res); | ||
} | ||
return collections; | ||
} | ||
if (resource) { | ||
return this.index[resource] ? this.index[resource] : false | ||
} | ||
return this.index | ||
}; | ||
/** | ||
* Get a JSON:API resource by type and ID. | ||
* | ||
* @example @lang js | ||
* const data = await this.$druxt.getResource('node--article', id) | ||
* | ||
* @param {string} type - The JSON:API Resource type. | ||
* @param {string} id - The Drupal resource UUID. | ||
* @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. | ||
* | ||
* @returns {object} The JSON:API resource data. | ||
*/ | ||
DruxtClient.prototype.getResource = async function getResource (type, id, query) { | ||
if (!id || !type) { | ||
return false | ||
} | ||
var ref = await this.getIndex(type); | ||
var href = ref.href; | ||
if (!href) { | ||
href = this.options.endpoint + '/' + type.replace('--', '/'); | ||
} | ||
var url = this.buildQueryUrl((href + "/" + id), query); | ||
try { | ||
var resource = await this.axios.get(url); | ||
return resource.data | ||
} catch (e) { | ||
return false | ||
} | ||
}; | ||
/** | ||
* DruxtClient options object. | ||
* | ||
* @typedef {object} DruxtClientOptions | ||
* | ||
* @param {object} [axios] - Axios instance settings. | ||
* @param {string} [endpoint=jsonapi] - The JSON:API endpoint. | ||
* @param {string} [jsonapiResourceConfig=jsonapi_resource_config--jsonapi_resource_config] - | ||
* The JSON:API resource config type, used for [JSON:API Extras](https://www.drupal.org/project/jsonapi_extras) support. | ||
* | ||
* @see {@link https://github.com/axios/axios#request-config} | ||
* | ||
* @example @lang js | ||
* { | ||
* axios: { | ||
* headers: { 'X-Custom-Header': true }, | ||
* }, | ||
* endpoint: 'api', | ||
* } | ||
*/ | ||
/** | ||
* @typedef {string|object} DruxtClientQuery | ||
* | ||
* A correctly formatted JSON:API query string or object. | ||
* | ||
* @example | ||
* page[limit]=5&page[offset]=5 | ||
* | ||
* @example @lang js | ||
* new DrupalJsonApiParams().addPageLimit(5) | ||
* | ||
* @see {@link https://www.npmjs.com/package/drupal-jsonapi-params} | ||
*/ | ||
/** | ||
* The DruxtModule base Vue.js component. | ||
* | ||
* Extend this component to build a Druxt module. | ||
* | ||
* @example @lang js | ||
* import { DruxtModule } from 'druxt' | ||
* export default { | ||
* name: 'DruxtTestModule', | ||
* extends: DruxtModule, | ||
* druxt: { | ||
* componentOptions: () => ([['wrapper']]), | ||
* propsData: (ctx) => ({ | ||
* bar: ctx.bar, | ||
* foo: ctx.foo, | ||
* }), | ||
* } | ||
* } | ||
*/ | ||
var script$2 = { | ||
components: { DruxtWrapper: __vue_component__$1 }, | ||
/** */ | ||
props: { | ||
/** | ||
* The module value. | ||
* | ||
* @type {(Array|Boolean|Date|Number|Object|String)} | ||
* @model | ||
*/ | ||
value: { | ||
type: [Array, Boolean, Date, Number, Object, String], | ||
default: null, | ||
}, | ||
wrapper: { | ||
type: Object, | ||
default: function () { return ({ | ||
component: 'div', | ||
propsData: {}, | ||
}); } | ||
}, | ||
}, | ||
/** | ||
* Loads the Druxt module data and applies a wrapper component as required. | ||
* | ||
* **Important:** If your component has an existing `fetch` method, you must manually invoke | ||
* the `DruxtModule.fetch()` hook. | ||
* | ||
* @example @lang js <caption>Manually invoking DruxtModule.fetch().</caption> | ||
* import { DruxtModule } from 'druxt' | ||
* export default { | ||
* name: 'DruxtTestModule', | ||
* extends: DruxtModule, | ||
* async fetch() { | ||
* await DruxtModule.fetch.call(this) | ||
* } | ||
* druxt: { | ||
* componentOptions: () => ([['wrapper']]), | ||
* propsData: (ctx) => ({ | ||
* bar: ctx.bar, | ||
* foo: ctx.foo, | ||
* }), | ||
* } | ||
* } | ||
*/ | ||
fetch: async function fetch() { | ||
if (!(this.$options || {}).druxt) { | ||
return false | ||
async getIndex(resource) { | ||
if (this.index && !resource) { | ||
return this.index; | ||
} | ||
// Build wrapper component object. | ||
var options = this.getModuleComponents(); | ||
var component = { | ||
is: (((options.filter(function (o) { return o.global; }) || [])[0] || {}).name || 'DruxtWrapper'), | ||
options: options.map(function (o) { return o.name; }) || [], | ||
}; | ||
// Get scoped slots. | ||
component.slots = Object.keys(this.getScopedSlots()); | ||
// Get wrapper data. | ||
var wrapperData = await this.getWrapperData(component.is); | ||
component.settings = wrapperData.druxt || {}; | ||
// Build wrapper component propsData. | ||
component = Object.assign({}, component, this.getModulePropsData(wrapperData.props)); | ||
// Set component data. | ||
this.component = component; | ||
}, | ||
/** | ||
* @property {ComponentData} component - The wrapper component and propsData to be rendered. | ||
* @property {object} model - The model object. | ||
*/ | ||
data: function (ref) { | ||
var value = ref.value; | ||
return ({ | ||
component: { | ||
$attrs: {}, | ||
is: 'DruxtWrapper', | ||
options: [], | ||
props: {}, | ||
propsData: {}, | ||
settings: {}, | ||
slots: [], | ||
}, | ||
model: value, | ||
}); | ||
}, | ||
/** */ | ||
methods: { | ||
/** | ||
* Get list of module wrapper components. | ||
* | ||
* @returns {Components} | ||
*/ | ||
getModuleComponents: function getModuleComponents() { | ||
var this$1 = this; | ||
if (!(this.$options.druxt || {}).componentOptions) { | ||
return [] | ||
} | ||
var options = this.$options.druxt.componentOptions.call(this, this); | ||
if (!options || !options.length) { | ||
return [] | ||
} | ||
// Build list of available components. | ||
var components = []; | ||
var loop = function () { | ||
var variants = []; | ||
components = components.concat( set.map(function (item) { | ||
// Build array of name parts. | ||
var parts = variants.length ? [].concat( variants[0].parts ) : []; | ||
parts.push(pascalCase(splitByCase(item))); | ||
// Convert parts into a pascalCase component name. | ||
var name = pascalCase([this$1.$options.name ].concat( parts)); | ||
// Check if component is globally registered. | ||
var global = !!this$1.$options.components[name]; | ||
// Store set variant data to be used in next set item. | ||
variants.unshift({ global: global, name: name, parts: parts }); | ||
return { global: global, name: name, parts: parts } | ||
})); | ||
}; | ||
for (var set of options.filter(function (set) { return Array.isArray(set); })) loop(); | ||
// Filter unique components. | ||
var unique = components.filter((function (s) { return function (o) { return !s.has(o.name) && s.add(o.name); }; })(new Set)); | ||
// Sort items by parts length. | ||
var sorted = unique.sort(function (a, b) { return b.parts.length - a.parts.length; }); | ||
return sorted | ||
}, | ||
/** | ||
* Get module propsData via modules `druxt.propsData()` callback. | ||
* | ||
* @example @lang js | ||
* { | ||
* bar: 'foo', | ||
* foo: 'bar', | ||
* } | ||
* | ||
* @return {object} | ||
*/ | ||
getModulePropsData: function getModulePropsData(wrapperProps) { | ||
if ( wrapperProps === void 0 ) wrapperProps = {}; | ||
if (!(this.$options.druxt || {}).propsData) { | ||
return {} | ||
} | ||
var propsData = this.$options.druxt.propsData.call(this, this); | ||
// Props. | ||
var props = {}; | ||
var propsKeys = Object.keys(wrapperProps).filter(function (i) { return Object.keys(propsData).includes(i); }); | ||
for (var key of propsKeys) { | ||
props[key] = propsData[key]; | ||
} | ||
// $attrs. | ||
var $attrs = Object.assign({}, this.$attrs); | ||
delete $attrs['data-fetch-key']; | ||
var $attrsKeys = Object.keys(propsData).filter(function (i) { return !Object.keys(wrapperProps).includes(i); }); | ||
for (var key$1 of $attrsKeys) { | ||
$attrs[key$1] = propsData[key$1]; | ||
} | ||
return { $attrs: $attrs, props: props, propsData: propsData } | ||
}, | ||
/** | ||
* Gets a Druxt modules scoped slots, and if there's no default slots, | ||
* provides a develop mode debug default or passes through to a | ||
* default template. | ||
* | ||
* @return {object} | ||
*/ | ||
getScopedSlots: function getScopedSlots() { | ||
var this$1 = this; | ||
var h = this.$createElement; | ||
var scopedSlots = typeof (this.$options.druxt || {}).slots === 'function' | ||
? this.$options.druxt.slots.call(this, h) | ||
: {}; | ||
// Pass through default scoped slot if provided. | ||
if (typeof this.$scopedSlots.default === 'function') { | ||
scopedSlots.default = function (attrs) { return this$1.$scopedSlots.default(Object.assign({}, ((this$1.$options.druxt || {}).propsData || (function () {}))(this$1), | ||
attrs)); }; | ||
} | ||
// Provide debug data if Nuxt is running in dev mode. | ||
if (!scopedSlots.default && this.$nuxt.context.isDev) { | ||
scopedSlots.default = function () { return h( | ||
'details', | ||
{ | ||
style: { | ||
border: '2px dashed lightgrey', | ||
margin: '0.5em 0', | ||
padding: '1em', | ||
}, | ||
}, | ||
[ | ||
h('summary', [("[" + (this$1.$options._componentTag) + "] Missing default slot.")]), | ||
h('label', ['Component options:', h('ul', this$1.component.options.map(function (s) { return h('li', [s]); }))]), | ||
h('br'), | ||
h('label', ['propsData:', h('pre', [h('code', [JSON.stringify(this$1.component.propsData, null, '\t')])])]) | ||
] | ||
); }; | ||
} | ||
return scopedSlots | ||
}, | ||
/** | ||
* Get wrapper component data. | ||
* | ||
* @param {string} component - The Wrapper component name. | ||
* | ||
* @return {WrapperData} | ||
*/ | ||
getWrapperData: async function getWrapperData(component) { | ||
var wrapperData = { druxt: {}, props: {} }; | ||
if (!this.$options.components[component]) { | ||
return wrapperData | ||
} | ||
// Get data from resolved component. | ||
if (this.$options.components[component].options) { | ||
wrapperData = this.$options.components[component].options; | ||
} | ||
// Get data from unresolved component. | ||
else if (typeof this.$options.components[component] === 'function' && this._init) { | ||
wrapperData = (await this.$options.components[component].call(this)) || {}; | ||
} | ||
var options = Vue.util.mergeOptions({}, wrapperData); | ||
return { | ||
druxt: options.druxt || {}, | ||
props: options.props || {}, | ||
} | ||
if (this.index && resource) { | ||
return this.index[resource] ? this.index[resource] : false; | ||
} | ||
}, | ||
watch: { | ||
model: function model() { | ||
if (this.component.props.value !== this.model) { | ||
this.component.props.value = this.model; | ||
// Only emit 'input' if using the default 'DruxtWrapper' component. | ||
if (this.component.is === 'DruxtWrapper') { | ||
this.$emit('input', this.model); | ||
} | ||
const index = await this.axios.get(this.options.endpoint); | ||
this.index = index.data.links; | ||
if (this.index[this.options.jsonapiResourceConfig]) { | ||
const resources = await this.axios.get(this.index[this.options.jsonapiResourceConfig].href); | ||
for (const resourceType in resources.data.data) { | ||
const resource2 = resources.data.data[resourceType]; | ||
const internal = resource2.attributes.drupal_internal__id.split("--"); | ||
const item = { | ||
resourceType: resource2.attributes.resourceType, | ||
entityType: internal[0], | ||
bundle: internal[1], | ||
resourceFields: resource2.attributes.resourceFields | ||
}; | ||
const id = [item.entityType, item.bundle].join("--"); | ||
this.index[id] = { | ||
...item, | ||
...this.index[id] | ||
}; | ||
} | ||
}, | ||
value: function value() { | ||
if (this.value !== this.model) { | ||
this.model = this.value; | ||
} | ||
} | ||
}, | ||
render: function render(h) { | ||
var self = this; | ||
var wrapperData = { | ||
class: this.wrapper.class || undefined, | ||
style: this.wrapper.style || undefined, | ||
props: this.wrapper.propsData, | ||
}; | ||
// Return only wrapper if fetch state is still pending. | ||
if (this.$fetchState.pending) { | ||
return h(this.wrapper.component, wrapperData) | ||
if (resource) { | ||
return this.index[resource] ? this.index[resource] : false; | ||
} | ||
// Return wrapped component. | ||
var attrs = Object.assign({}, this.component.$attrs, this.$attrs); | ||
delete attrs['data-fetch-key']; | ||
return h(this.wrapper.component, wrapperData, [ | ||
h(this.component.is, { | ||
attrs: attrs, | ||
on: { | ||
input: function input(value) { | ||
self.model = value; | ||
self.$emit('input', value); | ||
} | ||
}, | ||
props: this.component.props, | ||
ref: 'component', | ||
scopedSlots: this.getScopedSlots(), | ||
}) | ||
]) | ||
return this.index; | ||
} | ||
}; | ||
async getResource(type, id, query) { | ||
if (!id || !type) { | ||
return false; | ||
} | ||
let { href } = await this.getIndex(type); | ||
if (!href) { | ||
href = this.options.endpoint + "/" + type.replace("--", "/"); | ||
} | ||
const url = this.buildQueryUrl(`${href}/${id}`, query); | ||
try { | ||
const resource = await this.axios.get(url); | ||
return resource.data; | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
} | ||
/** | ||
* @typedef {object[]} Components | ||
* @property {boolean} global - Component global registration state. | ||
* @property {string} name - The component name. | ||
* @property {string[]} parts - The component naming parts. | ||
* | ||
* @example @lang js | ||
* [{ | ||
* global: true, | ||
* pascal: 'DruxtTestModuleWrapper', | ||
* parts: ['Wrapper'], | ||
* }] | ||
*/ | ||
/** | ||
* @typedef {object} ComponentData | ||
* @property {object} $attrs - propsData not registered by the Wrapper component. | ||
* @property {string} is=DruxtWrapper - The Wrapper component name. | ||
* @property {string[]} options - The Wrapper component options. | ||
* @property {object} props - propsData registered by the Wrapper component. | ||
* @property {object} propsData - The component propsData object. | ||
* @property {object} settings - Druxt settings object provided by the Wrapper component. | ||
* | ||
* @example @lang js | ||
* { | ||
* $attrs: { bar: 'foo' }, | ||
* is: 'DruxtTestModuleWrapper', | ||
* options: [ | ||
* 'DruxtTestModuleWrapper', | ||
* ], | ||
* props: { foo: 'bar' }, | ||
* propsData: { | ||
* bar: 'foo', | ||
* foo: 'bar', | ||
* }, | ||
* settings: { fooBar: true }, | ||
* } | ||
*/ | ||
/** | ||
* @typedef {object} WrapperData | ||
* @property {object} druxt - Druxt settings object for use by Druxt module. | ||
* @property {object} props - Registered props oject. | ||
* | ||
* @example @lang js | ||
* { | ||
* druxt: { fooBar: true }, | ||
* props: { | ||
* foo: { | ||
* type: String, | ||
* default: '', | ||
* } | ||
* } | ||
* } | ||
*/ | ||
/* script */ | ||
var __vue_script__$2 = script$2; | ||
/* template */ | ||
/* style */ | ||
var __vue_inject_styles__$2 = undefined; | ||
/* scoped */ | ||
var __vue_scope_id__$2 = undefined; | ||
/* module identifier */ | ||
var __vue_module_identifier__$2 = undefined; | ||
/* functional template */ | ||
var __vue_is_functional_template__$2 = undefined; | ||
/* style inject */ | ||
/* style inject SSR */ | ||
/* style inject shadow dom */ | ||
var __vue_component__$2 = /*#__PURE__*/normalizeComponent( | ||
{}, | ||
__vue_inject_styles__$2, | ||
__vue_script__$2, | ||
__vue_scope_id__$2, | ||
__vue_is_functional_template__$2, | ||
__vue_module_identifier__$2, | ||
false, | ||
undefined, | ||
undefined, | ||
undefined | ||
); | ||
var getDrupalJsonApiParams = function (query) { | ||
const getDrupalJsonApiParams = (query) => { | ||
return new DrupalJsonApiParams().initialize(query); | ||
}; | ||
var dehydrateResources = function (ref) { | ||
var commit = ref.commit; | ||
var queryObject = ref.queryObject; | ||
var resources = ref.resources; | ||
return resources.map(function (data) { | ||
// Generate a query link for included resources. | ||
// This is used to determine if the resource is a partial. | ||
var link = decodeURI(((data.links || {}).self || {}).href || ''); | ||
var href = typeof (queryObject.fields || {})[data.type] === 'string' | ||
? [link.split('?')[0], ("fields[" + (data.type) + "]=" + (queryObject.fields[data.type]))].join('?') | ||
: link; | ||
// Commit the included resource. | ||
commit('druxt/addResource', { | ||
const dehydrateResources = ({ commit, queryObject, resources }) => { | ||
return resources.map((data) => { | ||
const link = decodeURI(((data.links || {}).self || {}).href || ""); | ||
const href = typeof (queryObject.fields || {})[data.type] === "string" ? [link.split("?")[0], `fields[${data.type}]=${queryObject.fields[data.type]}`].join("?") : link; | ||
commit("druxt/addResource", { | ||
resource: { | ||
data: data, | ||
links: { self: { href: href } }, | ||
data, | ||
links: { self: { href } } | ||
} | ||
}); | ||
return { id: data.id, type: data.type } | ||
}) | ||
return { id: data.id, type: data.type }; | ||
}); | ||
}; | ||
var DruxtStore = function (ref) { | ||
var store = ref.store; | ||
if (typeof store === 'undefined') { | ||
throw new TypeError('Vuex store not found.') | ||
const DruxtStore = ({ store }) => { | ||
if (typeof store === "undefined") { | ||
throw new TypeError("Vuex store not found."); | ||
} | ||
/** | ||
* @namespace | ||
*/ | ||
var namespace = 'druxt'; | ||
/** | ||
* The DruxtStore Vuex module. | ||
* | ||
* Provides a Vuex state object, mutations and actions for interacting with the DruxtClient. | ||
* | ||
* @name druxt | ||
* @module druxt | ||
*/ | ||
var module = { | ||
const namespace = "druxt"; | ||
const module = { | ||
namespaced: true, | ||
/** | ||
* Vuex State object. | ||
* | ||
* @name state | ||
* @type {object} | ||
* @property {DruxtClientCollections} collections - JSON:API resource collections store. | ||
* @property {object} resources - JSON:API resources store. | ||
* @readonly | ||
*/ | ||
state: function () { return ({ | ||
state: () => ({ | ||
collections: {}, | ||
resources: {} | ||
}); }, | ||
/** | ||
* Vuex Mutations. | ||
*/ | ||
}), | ||
mutations: { | ||
/** | ||
* @name addCollection | ||
* @mutator {object} addCollection=collections Adds a JSON:API collection to the Vuex state object. | ||
* @param {addCollectionContext} context | ||
* | ||
* @example @lang js | ||
* this.$store.commit('druxt/addCollection', { collection, type, hash }) | ||
*/ | ||
addCollection: function addCollection (state, ref) { | ||
var collection = ref.collection; | ||
var type = ref.type; | ||
var hash = ref.hash; | ||
if (!state.collections[type]) { Vue.set(state.collections, type, {}); } | ||
// Parse the query. | ||
var link = decodeURI((((collection || {}).links || {}).self || {}).href || ''); | ||
var query = link.split('?')[1] || ''; | ||
var queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
// Store and dehydrate collection resources. | ||
collection.data = dehydrateResources({ commit: this.commit, queryObject: queryObject, resources: collection.data }); | ||
// Extract and store included resources. | ||
addCollection(state, { collection, type, hash }) { | ||
if (!state.collections[type]) | ||
Vue.set(state.collections, type, {}); | ||
const link = decodeURI((((collection || {}).links || {}).self || {}).href || ""); | ||
const query = link.split("?")[1] || ""; | ||
const queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
collection.data = dehydrateResources({ commit: this.commit, queryObject, resources: collection.data }); | ||
if (collection.included) { | ||
collection.included = dehydrateResources({ commit: this.commit, queryObject: queryObject, resources: collection.included }); | ||
collection.included = dehydrateResources({ commit: this.commit, queryObject, resources: collection.included }); | ||
delete collection.included; | ||
} | ||
// Recursively merge new collection data into stored collection. | ||
collection = merge(state.collections[type][hash] || {}, collection, { arrayMerge: function (dst, src) { return src; } }); | ||
collection = merge(state.collections[type][hash] || {}, collection, { arrayMerge: (dst, src) => src }); | ||
Vue.set(state.collections[type], hash, collection); | ||
}, | ||
/** | ||
* @name addResource | ||
* @mutator {object} addResource=resources Adds a JSON:API resource to the Vuex state object. | ||
* @param {addResourceContext} context | ||
* | ||
* @example @lang js | ||
* this.$store.commit('druxt/addResource', { resource }) | ||
*/ | ||
addResource: function addResource (state, ref) { | ||
var resource = ref.resource; | ||
var hash = ref.hash; | ||
addResource(state, { resource, hash }) { | ||
if (hash) { | ||
console.warn('[druxt] The `hash` argument for `druxt/addResource` has been deprecated, see https://druxtjs.org/guide/deprecations.html#druxtstore-addresource-hash'); | ||
console.warn("[druxt] The `hash` argument for `druxt/addResource` has been deprecated, see https://druxtjs.org/guide/deprecations.html#druxtstore-addresource-hash"); | ||
} | ||
var ref$1 = (resource || {}).data || {}; | ||
var id = ref$1.id; | ||
var type = ref$1.type; | ||
const { id, type } = (resource || {}).data || {}; | ||
if (!id || !type) { | ||
// @TODO - Error? | ||
return | ||
return; | ||
} | ||
// Parse the query. | ||
var link = decodeURI((((resource || {}).links || {}).self || {}).href || ''); | ||
var query = link.split('?')[1] || ''; | ||
var queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
// Add cache flag to resource. | ||
var flag = typeof (queryObject.fields || {})[((resource || {}).data || {}).type] === 'string' ? '_druxt_partial' : '_druxt_full'; | ||
const link = decodeURI((((resource || {}).links || {}).self || {}).href || ""); | ||
const query = link.split("?")[1] || ""; | ||
const queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
const flag = typeof (queryObject.fields || {})[((resource || {}).data || {}).type] === "string" ? "_druxt_partial" : "_druxt_full"; | ||
resource[flag] = Date.now(); | ||
// Ensure Resource type array is reactive. | ||
if (!state.resources[type]) { Vue.set(state.resources, type, {}); } | ||
// Extract and store included data. | ||
if (!state.resources[type]) | ||
Vue.set(state.resources, type, {}); | ||
if (resource.included) { | ||
dehydrateResources({ commit: this.commit, queryObject: queryObject, resources: resource.included }); | ||
dehydrateResources({ commit: this.commit, queryObject, resources: resource.included }); | ||
delete resource.included; | ||
} | ||
// Recursively merge new resource data into stored resource. | ||
resource = merge(state.resources[type][id] || {}, resource, { arrayMerge: function (dst, src) { return src; } }); | ||
resource = merge(state.resources[type][id] || {}, resource, { arrayMerge: (dst, src) => src }); | ||
Vue.set(state.resources[type], id, resource); | ||
}, | ||
} | ||
}, | ||
/** | ||
* Vuex Actions. | ||
*/ | ||
actions: { | ||
/** | ||
* Get collection of resources. | ||
* | ||
* @name getCollection | ||
* @action getCollection | ||
* @param {getCollectionContext} context | ||
* @return {object[]} Array of Drupal JSON:API resource data. | ||
* | ||
* @example @lang js | ||
* // Load all currently published Articles. | ||
* const resources = await this.$store.dispatch('druxt/getCollection', { | ||
* type: 'node--article', | ||
* query: new DrupalJsonApiParams().addFilter('status', '1'), | ||
* }) | ||
*/ | ||
getCollection: async function getCollection (ref, ref$1) { | ||
var commit = ref.commit; | ||
var state = ref.state; | ||
var type = ref$1.type; | ||
var query = ref$1.query; | ||
// Generate a hash using query data excluding the 'fields' and 'include' data. | ||
var queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
var hash = query ? md5(JSON.stringify(Object.assign({}, queryObject, {fields: {}, include: []}))) : '_default'; | ||
// If collection hash exists, re-hydrate and return the data. | ||
async getCollection({ commit, state }, { type, query }) { | ||
const queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
const hash = query ? md5(JSON.stringify({ ...queryObject, fields: {}, include: [] })) : "_default"; | ||
if ((state.collections[type] || {})[hash]) { | ||
return Object.assign({}, state.collections[type][hash], | ||
// Hydrate resource data. | ||
{data: state.collections[type][hash].data.map(function (o) { return (state.resources[o.type][o.id] || {}).data; })}) | ||
return { | ||
...state.collections[type][hash], | ||
data: state.collections[type][hash].data.map((o) => (state.resources[o.type][o.id] || {}).data) | ||
}; | ||
} | ||
// Get the collection using the DruxtClient instance. | ||
var collection = await this.$druxt.getCollection(type, query); | ||
// Store the collection in the DruxtStore. | ||
commit('addCollection', { collection: Object.assign({}, collection), type: type, hash: hash }); | ||
return collection | ||
const collection = await this.$druxt.getCollection(type, query); | ||
commit("addCollection", { collection: { ...collection }, type, hash }); | ||
return collection; | ||
}, | ||
/** | ||
* Get JSON:API Resource. | ||
* | ||
* - Executes query against Drupal JSON:API. | ||
* - Caches result in the Vuex store. | ||
* - Returns cached result from Vuex store when available. | ||
* | ||
* @name getResource | ||
* @action getResource=resources | ||
* @param {getResourceContext} context | ||
* @return {object} The Drupal JSON:API resource. | ||
* | ||
* @example @lang js | ||
* const resource = await this.$store.dispatch('druxt/getResource', { type: 'node--article', id }) | ||
*/ | ||
getResource: async function getResource (ref, ref$1) { | ||
var commit = ref.commit; | ||
var dispatch = ref.dispatch; | ||
var state = ref.state; | ||
var type = ref$1.type; | ||
var id = ref$1.id; | ||
var query = ref$1.query; | ||
// Get the resource from the store if it's avaialble. | ||
var storedResource = (state.resources[type] || {})[id] ? | ||
Object.assign({}, state.resources[type][id]) | ||
: null; | ||
// Parse the query. | ||
var queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
queryObject.include = Array.isArray(queryObject.include) | ||
? queryObject.include.join(',') | ||
: queryObject.include; | ||
// Ensure that includes are in the fields filter. | ||
if (queryObject.include && typeof (queryObject.fields || {})[type] === 'string') { | ||
var fields$1 = queryObject.fields[type].split(',').filter(function (s) { return s; }); | ||
var includes = queryObject.include.split(',').filter(function (s) { return s && !s.includes('.'); }); | ||
queryObject.fields[type] = Array.from( | ||
new Set(fields$1.concat( includes)) | ||
).filter(function (s) { return s; }).join(','); | ||
async getResource({ commit, dispatch, state }, { type, id, query }) { | ||
const storedResource = (state.resources[type] || {})[id] ? { ...state.resources[type][id] } : null; | ||
const queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
queryObject.include = Array.isArray(queryObject.include) ? queryObject.include.join(",") : queryObject.include; | ||
if (queryObject.include && typeof (queryObject.fields || {})[type] === "string") { | ||
const fields2 = queryObject.fields[type].split(",").filter((s) => s); | ||
const includes = queryObject.include.split(",").filter((s) => s && !s.includes(".")); | ||
queryObject.fields[type] = Array.from(new Set([...fields2, ...includes])).filter((s) => s).join(","); | ||
} | ||
// Hydrate included data based on the include query. | ||
var included = []; | ||
let included = []; | ||
if (queryObject.include && storedResource) { | ||
// Request included resources from druxt/getResource. | ||
var resources = | ||
await Promise.all(queryObject.include.split(',') | ||
.filter(function (s) { return Object.keys((storedResource.data.relationships || {})).includes(s); }) | ||
.map(function (key) { | ||
var ref = storedResource.data.relationships[key]; | ||
var data = ref.data; | ||
data = Array.isArray(data) ? data : [data]; | ||
// Get any sub-includes, e.g., `media,media.image` becomes `image`. | ||
var include = queryObject.include.split(',') | ||
.filter(function (s) { return s.startsWith((key + ".")); }) | ||
.map(function (s) { return s.slice(key.length + 1); }) | ||
.join(','); | ||
return data.filter(function (o) { return typeof o === 'object' && o; }).map(function (o) { | ||
return dispatch('getResource', { | ||
type: o.type, | ||
id: o.id, | ||
query: Object.assign({}, queryObject, {include: include}), | ||
}) | ||
}) | ||
}) | ||
.flat() | ||
); | ||
// Merge all nested, included resources. | ||
for (var include of resources) { | ||
included = included.concat( [include.data], include.included || []); | ||
const resources = await Promise.all(queryObject.include.split(",").filter((s) => Object.keys(storedResource.data.relationships || {}).includes(s)).map((key) => { | ||
let { data } = storedResource.data.relationships[key]; | ||
data = Array.isArray(data) ? data : [data]; | ||
const include = queryObject.include.split(",").filter((s) => s.startsWith(`${key}.`)).map((s) => s.slice(key.length + 1)).join(","); | ||
return data.filter((o) => typeof o === "object" && o).map((o) => { | ||
return dispatch("getResource", { | ||
type: o.type, | ||
id: o.id, | ||
query: { ...queryObject, include } | ||
}); | ||
}); | ||
}).flat()); | ||
for (const include of resources) { | ||
included = [...included, include.data, ...include.included || []]; | ||
} | ||
storedResource.included = included; | ||
} | ||
// Return if we have the full resource. | ||
if ((storedResource || {})._druxt_full) { | ||
return storedResource | ||
return storedResource; | ||
} | ||
var isFull = typeof (queryObject.fields || {})[type] !== 'string'; | ||
// Determine if we have all the requested field data. | ||
var fields = isFull ? true : (queryObject.fields || {})[type]; | ||
const isFull = typeof (queryObject.fields || {})[type] !== "string"; | ||
let fields = isFull ? true : (queryObject.fields || {})[type]; | ||
if (storedResource && !isFull && fields) { | ||
var queryFields = fields.split(','); | ||
var resourceFields = Object.keys(((storedResource || {}).data || {}).attributes || {}).concat( Object.keys(((storedResource || {}).data || {}).relationships || {}) ); | ||
var missingFields = queryFields.filter(function (key) { return !resourceFields.includes(key); }); | ||
const queryFields = fields.split(","); | ||
const resourceFields = [ | ||
...Object.keys(((storedResource || {}).data || {}).attributes || {}), | ||
...Object.keys(((storedResource || {}).data || {}).relationships || {}) | ||
]; | ||
const missingFields = queryFields.filter((key) => !resourceFields.includes(key)); | ||
fields = !!missingFields.length; | ||
// Modify query to load additional fields, if required. | ||
queryObject.fields[type] = (missingFields || []).join(',') || undefined; | ||
queryObject.fields[type] = (missingFields || []).join(",") || void 0; | ||
} | ||
// Request the resource from the DruxtClient if required. | ||
var resource; | ||
let resource; | ||
if (!storedResource || fields) { | ||
resource = await this.$druxt.getResource(type, id, getDrupalJsonApiParams(queryObject)); | ||
commit('addResource', { resource: Object.assign({}, resource) }); | ||
commit("addResource", { resource: { ...resource } }); | ||
} | ||
// Build resource to be returned. | ||
var result = Object.assign({}, (state.resources[type] || {})[id]); | ||
// Merge included resources into resource. | ||
const result = { ...(state.resources[type] || {})[id] }; | ||
if (queryObject.include && ((resource || {}).included || (storedResource || {}).included)) { | ||
included = ( (resource || {}).included || [] ).concat( (storedResource || {}).included || [] ); | ||
result.included = Array.from(new Set(included.filter(function (o) { return (o || {}).id; }).map(function (o) { return o.id; }))) | ||
.map(function (id) { return included.find(function (o) { return o.id === id; }); }); | ||
included = [ | ||
...(resource || {}).included || [], | ||
...(storedResource || {}).included || [] | ||
]; | ||
result.included = Array.from(new Set(included.filter((o) => (o || {}).id).map((o) => o.id))).map((id2) => included.find((o) => o.id === id2)); | ||
} | ||
return result | ||
}, | ||
return result; | ||
} | ||
} | ||
}; | ||
store.registerModule(namespace, module, { | ||
@@ -1426,260 +302,53 @@ preserveState: Boolean(store.state[namespace]) | ||
/** | ||
* Parameters for the `addCollection` mutation. | ||
* | ||
* @typedef {object} addCollectionContext | ||
* | ||
* @param {object} collection - A collection of JSON:API resources. | ||
* @param {string} type - The JSON:API collection resource type. | ||
* @param {string} hash - An md5 hash of the query string. | ||
* | ||
* @example @lang js | ||
* { | ||
* collection: { | ||
* jsonapi: {}, | ||
* data: [{}], | ||
* links: {} | ||
* }, | ||
* type: 'node--page', | ||
* hash: '_default' | ||
* } | ||
*/ | ||
/** | ||
* Parameters for the `addResource` mutation. | ||
* | ||
* @typedef {object} addResourceContext | ||
* | ||
* @param {object} resource - The JSON:API resource. | ||
* | ||
* @example @lang js | ||
* { | ||
* resource: { | ||
* jsonapi: {}, | ||
* data: {}, | ||
* links: {} | ||
* }, | ||
* } | ||
*/ | ||
/** | ||
* Parameters for the `getCollection` action. | ||
* | ||
* @typedef {object} getCollectionContext | ||
* | ||
* @param {string} type - The JSON:API collection resource type. | ||
* @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. | ||
* | ||
* @example @lang js | ||
* { | ||
* type: 'node--page', | ||
* query: new DrupalJsonApiParams().addFilter('status', '1') | ||
* } | ||
*/ | ||
/** | ||
* Parameters for the `getResource` action. | ||
* | ||
* @typedef {object} getResourceContext | ||
* | ||
* @param {string} type - The JSON:API Resource type. | ||
* @param {string} id - The Drupal resource UUID. | ||
* @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. | ||
* | ||
* @example @lang js | ||
* { | ||
* type: 'node--page', | ||
* id: 'd8dfd355-7f2f-4fc3-a149-288e4e293bdd' | ||
* } | ||
*/ | ||
/** | ||
* @typedef {string|object} DruxtClientQuery | ||
* | ||
* A correctly formatted JSON:API query string or object. | ||
* | ||
* @example | ||
* page[limit]=5&page[offset]=5 | ||
* | ||
* @example @lang js | ||
* new DrupalJsonApiParams().addPageLimit(5) | ||
* | ||
* @see {@link https://www.npmjs.com/package/drupal-jsonapi-params} | ||
*/ | ||
/** | ||
* Druxt utility class. | ||
* | ||
* @deprecated | ||
* @private | ||
*/ | ||
var DruxtClass = function DruxtClass() {}; | ||
/** | ||
* Get component data from available options. | ||
* | ||
* @param {object} vm - The DruxtJS module Vue.js component. | ||
* @param {string[]} options - The component naming options. | ||
* @param {boolean} [all=false] - Returns all options if true, else only globally registered options. | ||
* @param {string} [prefix] - A string to prefix all components. | ||
* | ||
* @returns {WrapperComponents} | ||
*/ | ||
DruxtClass.prototype.getComponents = function getComponents (vm, options, all, prefix) { | ||
if ( all === void 0 ) all = false; | ||
var results = []; | ||
var unique = {}; | ||
options | ||
// Filter out incorrectly typed items. | ||
.filter(function (item) { return Array.isArray(item); }) | ||
// Process each item. | ||
.map(function (item) { | ||
var variants = []; | ||
item.map(function (string) { | ||
var parts = variants.length ? [].concat( variants[0].parts ) : []; | ||
class DruxtClass { | ||
constructor() { | ||
} | ||
getComponents(vm, options, all = false, prefix) { | ||
const results = []; | ||
const unique = {}; | ||
options.filter((item) => Array.isArray(item)).map((item) => { | ||
const variants = []; | ||
item.map((string) => { | ||
const parts = variants.length ? [...variants[0].parts] : []; | ||
parts.push(string); | ||
var clone = [].concat( parts ); | ||
// Attach prefix as required. | ||
if (typeof prefix !== 'string' && (prefix !== false || typeof prefix === 'undefined') && ((vm || {}).$options || {}).name) { | ||
prefix = vm.$options.name.match(/[A-Z][a-z]+/g).map(function (word) { return word.toLowerCase(); }).join('-'); | ||
const clone = [...parts]; | ||
if (typeof prefix !== "string" && (prefix !== false || typeof prefix === "undefined") && ((vm || {}).$options || {}).name) { | ||
prefix = vm.$options.name.match(/[A-Z][a-z]+/g).map((word) => word.toLowerCase()).join("-"); | ||
} | ||
if (prefix) { | ||
clone.unshift(prefix); | ||
} | ||
// Generate component name values. | ||
var kebab = clone.map(function (string) { return string.toLowerCase().replace(/--|_/g, '-'); }).join('-'); | ||
var pascal = kebab.replace(/((\b|[^a-zA-Z0-9]+)[a-zA-Z0-9])/gi, function (match, p1, p2) { return match.toUpperCase().replace(p2, ''); }); | ||
// Check if component is globally registered. | ||
var global = false; | ||
for (var name of [kebab, pascal]) { | ||
if (typeof (((vm || {}).$options || {}).components || {})[name] !== 'undefined') { | ||
const kebab = clone.map((string2) => string2.toLowerCase().replace(/--|_/g, "-")).join("-"); | ||
const pascal = kebab.replace(/((\b|[^a-zA-Z0-9]+)[a-zA-Z0-9])/gi, (match, p1, p2) => match.toUpperCase().replace(p2, "")); | ||
let global = false; | ||
for (const name of [kebab, pascal]) { | ||
if (typeof (((vm || {}).$options || {}).components || {})[name] !== "undefined") { | ||
global = true; | ||
break | ||
break; | ||
} | ||
} | ||
variants.unshift({ global: global, kebab: kebab, parts: parts, pascal: pascal, prefix: prefix }); | ||
variants.unshift({ global, kebab, parts, pascal, prefix }); | ||
}); | ||
// Add variants to results. | ||
variants.map(function (variant) { | ||
// Ensure unique results. | ||
variants.map((variant) => { | ||
if (unique[variant.pascal]) { | ||
return | ||
return; | ||
} | ||
unique[variant.pascal] = true; | ||
results.push(variant); | ||
}); | ||
}); | ||
// Return globally registered components or all. | ||
return results | ||
.filter(function (option) { return option.global || !!all; }) | ||
.sort(function (a, b) { return b.parts.length - a.parts.length; }) | ||
}; | ||
/** | ||
* Get the Druxt module data from the referenced component. | ||
* | ||
* @returns {ModuleData} | ||
*/ | ||
DruxtClass.prototype.getModuleData = async function getModuleData (vm) { | ||
if (typeof ((vm || {}).$options || {}).druxt !== 'function') { | ||
return false | ||
return results.filter((option) => option.global || !!all).sort((a, b) => b.parts.length - a.parts.length); | ||
} | ||
var moduleData = await vm.$options.druxt({ vm: vm }); | ||
if ((vm.$options || {}).name) { | ||
moduleData.name = vm.$options.name.match(/[A-Z][a-z]+/g).map(function (word) { return word.toLowerCase(); }).join('-'); | ||
} | ||
return moduleData | ||
}; | ||
/** | ||
* @name DruxtComponentMixin | ||
* @deprecated | ||
* @private | ||
*/ | ||
var DruxtComponentMixin = { | ||
components: { DruxtWrapper: __vue_component__$1 }, | ||
/** | ||
* @property {Component} component - The wrapper component and propsData to be rendered. | ||
*/ | ||
data: function () { return ({ | ||
component: { | ||
is: 'DruxtWrapper', | ||
options: [], | ||
propsData: {}, | ||
}, | ||
}); }, | ||
props: { | ||
wrapper: { | ||
type: Object, | ||
default: function () { return ({ | ||
component: 'div', | ||
propsData: {}, | ||
}); } | ||
async getModuleData(vm) { | ||
if (typeof ((vm || {}).$options || {}).druxt !== "function") { | ||
return false; | ||
} | ||
}, | ||
/** | ||
* The Nuxt Fetch hook. | ||
* | ||
* Loads the Druxt module data and applies a wrapper component as required. | ||
* | ||
* **Important:** If your component has an existing `fetch` method, you must manually invoke | ||
* the `DruxtComponentMixin.fetch()` hook. | ||
* | ||
* @see {@link https://nuxtjs.org/api/pages-fetch/} | ||
* | ||
* @example @lang vue <caption>Manually invoking DruxtComponentMixin.fetch().</caption> | ||
* <script> | ||
* import { DruxtComponentMixin } from 'druxt' | ||
* export default { | ||
* mixins: [DruxtComponentMixin], | ||
* | ||
* async fetch { | ||
* await DruxtComponentMixin.fetch.call(this) | ||
* } | ||
* } | ||
* </script> | ||
*/ | ||
fetch: async function fetch() { | ||
console.warn('DruxtComponentMixin is deprecated in favour of DruxtModule.'); | ||
// @todo - check for this.$druxt plugin. | ||
var druxt = new DruxtClass(); | ||
var moduleData = await druxt.getModuleData(this); | ||
this.component.propsData = moduleData.propsData || {}; | ||
if (!moduleData.componentOptions) { | ||
return | ||
const moduleData = await vm.$options.druxt({ vm }); | ||
if ((vm.$options || {}).name) { | ||
moduleData.name = vm.$options.name.match(/[A-Z][a-z]+/g).map((word) => word.toLowerCase()).join("-"); | ||
} | ||
return moduleData; | ||
} | ||
} | ||
var options = druxt.getComponents(this, moduleData.componentOptions, true); | ||
this.component.options = options.map(function (item) { return item.pascal; }); | ||
var available = options.filter(function (item) { return item.global; }); | ||
if (!available.length) { | ||
return | ||
} | ||
this.component.is = available[0].pascal; | ||
}, | ||
}; | ||
export default DruxtNuxtModule; | ||
export { __vue_component__ as Druxt, DruxtClass, DruxtClient, DruxtComponentMixin, __vue_component__$2 as DruxtModule, DruxtStore, __vue_component__$1 as DruxtWrapper }; | ||
export { DruxtClass, DruxtClient, DruxtStore, DruxtNuxtModule as default }; |
@@ -1,1392 +0,308 @@ | ||
'use strict';Object.defineProperty(exports,'__esModule',{value:true});function _interopDefault(e){return(e&&(typeof e==='object')&&'default'in e)?e['default']:e}var path=require('path'),axios=_interopDefault(require('axios')),querystring=require('querystring'),scule=require('scule'),Vue=_interopDefault(require('vue')),merge=_interopDefault(require('deepmerge')),md5=_interopDefault(require('md5')),drupalJsonapiParams=require('drupal-jsonapi-params');/** | ||
* The Vue.js Druxt component. | ||
* | ||
* @example @lang vue | ||
* <Druxt | ||
* :module="module" | ||
* :props-data="propsData" | ||
* :wrapper="{ | ||
* component, | ||
* propsData: {} | ||
* }" | ||
* /> | ||
*/ | ||
var script = { | ||
name: 'Druxt', | ||
'use strict'; | ||
/** | ||
* Vue.js Props. | ||
*/ | ||
props: { | ||
/** | ||
* Inner element. | ||
* | ||
* @type {object} | ||
* @default { component: 'div', propsData: {} } | ||
*/ | ||
inner: { | ||
type: [Object, Boolean], | ||
default: function () { return ({ | ||
component: 'div', | ||
propsData: {}, | ||
}); } | ||
}, | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
/** | ||
* The DruxtJS module to render. | ||
* | ||
* @type {string} | ||
* | ||
* @example @lang vue <caption>Using the [DruxtJS Site module](https://site.druxtjs.org).</caption> | ||
* <Druxt module="site" /> | ||
*/ | ||
module: { | ||
type: String, | ||
required: true, | ||
}, | ||
const path = require('path'); | ||
const axios = require('axios'); | ||
const querystring = require('querystring'); | ||
const merge = require('deepmerge'); | ||
const md5 = require('md5'); | ||
const Vue = require('vue'); | ||
const drupalJsonapiParams = require('drupal-jsonapi-params'); | ||
/** | ||
* Props data to bind to the specified DruxtJS module. | ||
* | ||
* @type {object} | ||
* | ||
* @example @lang vue <caption>Using the [DruxtJS Entity module](https://entity.druxtjs.org) to render a 'node--article' resource.</caption> | ||
* <Druxt | ||
* module="entity" | ||
* :props-data="{ | ||
* mode: 'teaser', | ||
* type: 'node--article', | ||
* uuid | ||
* }" | ||
* /> | ||
*/ | ||
propsData: { | ||
type: Object, | ||
default: function () { return ({}); } | ||
}, | ||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
/** | ||
* The module value. | ||
* | ||
* @type {(Array|Boolean|Date|Number|Object|String)} | ||
* @model | ||
*/ | ||
value: { | ||
type: [Array, Boolean, Date, Number, Object, String], | ||
default: null, | ||
}, | ||
const axios__default = /*#__PURE__*/_interopDefaultLegacy(axios); | ||
const merge__default = /*#__PURE__*/_interopDefaultLegacy(merge); | ||
const md5__default = /*#__PURE__*/_interopDefaultLegacy(md5); | ||
const Vue__default = /*#__PURE__*/_interopDefaultLegacy(Vue); | ||
/** | ||
* Wrapper element. | ||
* | ||
* @type {object} | ||
* @default { component: 'div', propsData: {} } | ||
*/ | ||
wrapper: { | ||
type: [Object, Boolean], | ||
default: function () { return ({ | ||
component: 'div', | ||
propsData: {}, | ||
}); } | ||
}, | ||
}, | ||
/** | ||
* Vue.js Data object. | ||
* | ||
* @property {objects} components - The module and wrapper components settinsg. | ||
* @property {object} model - The model object. | ||
*/ | ||
data: function (ref) { | ||
var value = ref.value; | ||
return ({ | ||
component: { | ||
is: undefined, | ||
propsData: {}, | ||
}, | ||
model: value, | ||
const DruxtNuxtModule = function(moduleOptions = {}) { | ||
const options = { | ||
...moduleOptions, | ||
...(this.options || {}).druxt | ||
}; | ||
this.addPlugin({ | ||
src: path.resolve(__dirname, "../nuxt/plugin.js"), | ||
fileName: "druxt.js", | ||
options | ||
}); | ||
}, | ||
this.addPlugin({ | ||
src: path.resolve(__dirname, "../nuxt/store.js"), | ||
fileName: "store/druxt.js", | ||
options | ||
}); | ||
this.options.store = true; | ||
}; | ||
DruxtNuxtModule.meta = require("../package.json"); | ||
created: function created() { | ||
this.setModuleComponent(); | ||
}, | ||
methods: { | ||
/** | ||
* Sets the module component and propsData. | ||
*/ | ||
setModuleComponent: function setModuleComponent() { | ||
var component = "Druxt" + (this.module.split('-').map(function (string) { return string.charAt(0).toUpperCase() + string.slice(1); }).join('')); | ||
if (!this.$options.components[component]) { | ||
return | ||
} | ||
// Set component data. | ||
this.component.is = component; | ||
this.component.propsData = this.propsData; | ||
}, | ||
}, | ||
watch: { | ||
model: function model() { | ||
if (this.value !== this.model) { | ||
this.$emit('input', this.model); | ||
} | ||
}, | ||
value: function value() { | ||
if (this.value !== this.model) { | ||
this.model = this.value; | ||
} | ||
class DruxtClient { | ||
constructor(baseUrl, options = {}) { | ||
if (!baseUrl) { | ||
throw new Error("The 'baseUrl' parameter is required."); | ||
} | ||
}, | ||
render: function render(h) { | ||
var component = h(this.component.is, { | ||
props: Object.assign({}, {wrapper: this.inner}, | ||
this.component.propsData, | ||
this.$attrs, | ||
{value: this.model}), | ||
ref: 'module', | ||
}); | ||
if ((this.wrapper || {}).component) { | ||
return h(this.wrapper.component, { props: this.wrapper.propsData }, [component]) | ||
let axiosSettings = { baseURL: baseUrl }; | ||
if (typeof options.axios === "object") { | ||
axiosSettings = Object.assign(axiosSettings, options.axios); | ||
delete options.axios; | ||
} | ||
return component | ||
this.axios = axios__default['default'].create(axiosSettings); | ||
this.options = { | ||
endpoint: "/jsonapi", | ||
jsonapiResourceConfig: "jsonapi_resource_config--jsonapi_resource_config", | ||
...options | ||
}; | ||
} | ||
};function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { | ||
if (typeof shadowMode !== 'boolean') { | ||
createInjectorSSR = createInjector; | ||
createInjector = shadowMode; | ||
shadowMode = false; | ||
addHeaders(headers) { | ||
if (typeof headers === "undefined") { | ||
return false; | ||
} | ||
// Vue.extend constructor export interop. | ||
var options = typeof script === 'function' ? script.options : script; | ||
// render functions | ||
if (template && template.render) { | ||
options.render = template.render; | ||
options.staticRenderFns = template.staticRenderFns; | ||
options._compiled = true; | ||
// functional template | ||
if (isFunctionalTemplate) { | ||
options.functional = true; | ||
} | ||
for (const name in headers) { | ||
this.axios.defaults.headers.common[name] = headers[name]; | ||
} | ||
// scopedId | ||
if (scopeId) { | ||
options._scopeId = scopeId; | ||
} | ||
buildQueryUrl(url, query) { | ||
if (!query) { | ||
return url; | ||
} | ||
var hook; | ||
if (moduleIdentifier) { | ||
// server build | ||
hook = function (context) { | ||
// 2.3 injection | ||
context = | ||
context || // cached call | ||
(this.$vnode && this.$vnode.ssrContext) || // stateful | ||
(this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional | ||
// 2.2 with runInNewContext: true | ||
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { | ||
context = __VUE_SSR_CONTEXT__; | ||
} | ||
// inject component styles | ||
if (style) { | ||
style.call(this, createInjectorSSR(context)); | ||
} | ||
// register component module identifier for async chunk inference | ||
if (context && context._registeredComponents) { | ||
context._registeredComponents.add(moduleIdentifier); | ||
} | ||
}; | ||
// used by ssr in case component is cached and beforeCreate | ||
// never gets called | ||
options._ssrRegister = hook; | ||
if (typeof query === "string") { | ||
return query.charAt(0) === "?" ? url + query : [url, query].join("?"); | ||
} | ||
else if (style) { | ||
hook = shadowMode | ||
? function (context) { | ||
style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot)); | ||
} | ||
: function (context) { | ||
style.call(this, createInjector(context)); | ||
}; | ||
if (typeof query === "object" && typeof query.getQueryString === "function" && (query = query.getQueryString())) { | ||
return [url, query].join("?"); | ||
} | ||
if (hook) { | ||
if (options.functional) { | ||
// register for functional component in vue file | ||
var originalRender = options.render; | ||
options.render = function renderWithStyleInjection(h, context) { | ||
hook.call(context); | ||
return originalRender(h, context); | ||
}; | ||
} | ||
else { | ||
// inject component registration as beforeCreate hook | ||
var existing = options.beforeCreate; | ||
options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; | ||
} | ||
if (typeof query === "object" && Object.keys(query).length) { | ||
return [url, querystring.stringify(query)].join("?"); | ||
} | ||
return script; | ||
}/* script */ | ||
var __vue_script__ = script; | ||
/* template */ | ||
/* style */ | ||
var __vue_inject_styles__ = undefined; | ||
/* scoped */ | ||
var __vue_scope_id__ = undefined; | ||
/* module identifier */ | ||
var __vue_module_identifier__ = "data-v-9ca6675a"; | ||
/* functional template */ | ||
var __vue_is_functional_template__ = undefined; | ||
/* style inject */ | ||
/* style inject SSR */ | ||
/* style inject shadow dom */ | ||
var __vue_component__ = /*#__PURE__*/normalizeComponent( | ||
{}, | ||
__vue_inject_styles__, | ||
__vue_script__, | ||
__vue_scope_id__, | ||
__vue_is_functional_template__, | ||
__vue_module_identifier__, | ||
false, | ||
undefined, | ||
undefined, | ||
undefined | ||
);// | ||
// | ||
// | ||
// | ||
// | ||
// | ||
/** | ||
* The default Druxt module wrapper Vue.js component. | ||
* | ||
* This component is used by the Druxt component if an appropriate component can not be found | ||
* for the specified module. | ||
* | ||
* @private | ||
*/ | ||
var script$1 = { | ||
name: 'DruxtWrapper' | ||
};/* script */ | ||
var __vue_script__$1 = script$1; | ||
/* template */ | ||
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_vm._t("default")],2)}; | ||
var __vue_staticRenderFns__ = []; | ||
/* style */ | ||
var __vue_inject_styles__$1 = undefined; | ||
/* scoped */ | ||
var __vue_scope_id__$1 = undefined; | ||
/* module identifier */ | ||
var __vue_module_identifier__$1 = "data-v-35409ca9"; | ||
/* functional template */ | ||
var __vue_is_functional_template__$1 = false; | ||
/* style inject */ | ||
/* style inject SSR */ | ||
/* style inject shadow dom */ | ||
var __vue_component__$1 = /*#__PURE__*/normalizeComponent( | ||
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, | ||
__vue_inject_styles__$1, | ||
__vue_script__$1, | ||
__vue_scope_id__$1, | ||
__vue_is_functional_template__$1, | ||
__vue_module_identifier__$1, | ||
false, | ||
undefined, | ||
undefined, | ||
undefined | ||
);/** | ||
* Nuxt module function to install Druxt. | ||
* | ||
* @param {ModuleOptions} moduleOptions - DruxtJS module options. | ||
* | ||
* @example <caption>Nuxt configuration with module options</caption> @lang js | ||
* module.exports = { | ||
* modules: [ | ||
* ['druxt', { baseUrl: 'https://demo-api.druxtjs.org' }] | ||
* ] | ||
* } | ||
* | ||
* @example <caption>Nuxt configuration with root level options</caption> @lang js | ||
* module.exports = { | ||
* modules: [ | ||
* 'druxt' | ||
* ], | ||
* druxt: { | ||
* baseUrl: 'https://demo-api.druxtjs.org' | ||
* } | ||
* } | ||
*/ | ||
var DruxtNuxtModule = function (moduleOptions) { | ||
if ( moduleOptions === void 0 ) moduleOptions = {}; | ||
var options = Object.assign({}, moduleOptions, | ||
(this.options || {}).druxt); | ||
// Add plugin. | ||
this.addPlugin({ | ||
src: path.resolve(__dirname, '../nuxt/plugin.js'), | ||
fileName: 'druxt.js', | ||
options: options | ||
}); | ||
// Add Vuex plugin. | ||
this.addPlugin({ | ||
src: path.resolve(__dirname, '../nuxt/store.js'), | ||
fileName: 'store/druxt.js', | ||
options: options | ||
}); | ||
// Enable Vuex Store. | ||
this.options.store = true; | ||
}; | ||
DruxtNuxtModule.meta = require('../package.json'); | ||
/** | ||
* Module options object. | ||
* | ||
* @typedef {object} ModuleOptions | ||
* @property {string} baseUrl - The Base URL of the Drupal JSON:API backend. | ||
*//** | ||
* Druxt JSON:API client. | ||
*/ | ||
var DruxtClient = function DruxtClient(baseUrl, options) { | ||
if ( options === void 0 ) options = {}; | ||
// Check for URL. | ||
if (!baseUrl) { | ||
throw new Error('The \'baseUrl\' parameter is required.') | ||
return url; | ||
} | ||
checkPermissions(res) { | ||
if (res.data.meta && res.data.meta.omitted) { | ||
const permissions = {}; | ||
delete res.data.meta.omitted.links.help; | ||
for (const key in res.data.meta.omitted.links) { | ||
const link = res.data.meta.omitted.links[key]; | ||
const match = link.meta.detail.match(/'(.*?)'/); | ||
if (match && match[1]) { | ||
permissions[match[1]] = true; | ||
} | ||
} | ||
if (Object.keys(permissions).length) { | ||
throw new TypeError(`${res.data.meta.omitted.detail} | ||
// Setup Axios. | ||
var axiosSettings = { baseURL: baseUrl }; | ||
if (typeof options.axios === 'object') { | ||
axiosSettings = Object.assign(axiosSettings, options.axios); | ||
delete options.axios; | ||
} | ||
/** | ||
* The Axios instance. | ||
* | ||
* @see {@link https://github.com/axios/axios#instance-methods} | ||
* @type {object} | ||
*/ | ||
this.axios = axios.create(axiosSettings); | ||
/** | ||
* Druxt base options. | ||
* @type {object} | ||
* @private | ||
*/ | ||
this.options = Object.assign({}, {endpoint: '/jsonapi', | ||
jsonapiResourceConfig: 'jsonapi_resource_config--jsonapi_resource_config'}, | ||
options); | ||
}; | ||
/** | ||
* Add headers to the Axios instance. | ||
* | ||
* @example @lang js | ||
* this.$druxt.addHeaders({ 'Authorization': `Basic ${token}` }) | ||
* | ||
* @param {object} headers - An object containing HTTP headers. | ||
*/ | ||
DruxtClient.prototype.addHeaders = function addHeaders (headers) { | ||
if (typeof headers === 'undefined') { | ||
return false | ||
} | ||
for (var name in headers) { | ||
this.axios.defaults.headers.common[name] = headers[name]; | ||
} | ||
}; | ||
/** | ||
* Build query URL. | ||
* | ||
* @example @lang js | ||
* const query = new DrupalJsonApiParams() | ||
* query.addFilter('status', '1') | ||
* const queryUrl = this.$druxt.buildQueryUrl(resourceUrl, query) | ||
* | ||
* @param {string} url - The base query URL. | ||
* @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. | ||
* | ||
* @return {string} The URL with query string. | ||
*/ | ||
DruxtClient.prototype.buildQueryUrl = function buildQueryUrl (url, query) { | ||
if (!query) { | ||
return url | ||
} | ||
// If Query is string... | ||
if (typeof query === 'string') { | ||
return query.charAt(0) === '?' ? url + query : [url, query].join('?') | ||
} | ||
// If Query is object with 'getQueryString' function, (e.g., drupal-jsonapi-params)... | ||
if (typeof query === 'object' && typeof query.getQueryString === 'function' && (query = query.getQueryString())) { | ||
return [url, query].join('?') | ||
} | ||
// If query is object... | ||
if (typeof query === 'object' && Object.keys(query).length) { | ||
return [url, querystring.stringify(query)].join('?') | ||
} | ||
// Else... | ||
return url | ||
}; | ||
/** | ||
* Check response for permissions. | ||
* | ||
* @todo - Move this to utils? | ||
* | ||
* @param {object} res - Axios GET request response object. | ||
* | ||
* @private | ||
*/ | ||
DruxtClient.prototype.checkPermissions = function checkPermissions (res) { | ||
// Error handling: Required permissions. | ||
if (res.data.meta && res.data.meta.omitted) { | ||
var permissions = {}; | ||
delete res.data.meta.omitted.links.help; | ||
for (var key in res.data.meta.omitted.links) { | ||
var link = res.data.meta.omitted.links[key]; | ||
var match = link.meta.detail.match(/'(.*?)'/); | ||
if (match && match[1]) { | ||
permissions[match[1]] = true; | ||
Required permissions: ${Object.keys(permissions).join(", ")}.`); | ||
} | ||
} | ||
if (Object.keys(permissions).length) { | ||
throw new TypeError(((res.data.meta.omitted.detail) + "\n\n Required permissions: " + (Object.keys(permissions).join(', ')) + ".")) | ||
} | ||
async getCollection(type, query) { | ||
const { href } = await this.getIndex(type); | ||
if (!href) { | ||
return false; | ||
} | ||
const url = this.buildQueryUrl(href, query); | ||
const res = await this.axios.get(url); | ||
this.checkPermissions(res); | ||
return res.data; | ||
} | ||
}; | ||
/** | ||
* Get a collection of resources from the JSON:API server. | ||
* | ||
* @param {string} type - The JSON:API Resource type. | ||
* @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. | ||
* | ||
* @returns {object} The JSON:API collection response. | ||
* | ||
* @example @lang js | ||
* const collection = await this.$druxt.getCollection('node--recipe') | ||
*/ | ||
DruxtClient.prototype.getCollection = async function getCollection (type, query) { | ||
var ref = await this.getIndex(type); | ||
var href = ref.href; | ||
if (!href) { | ||
return false | ||
} | ||
var url = this.buildQueryUrl(href, query); | ||
var res = await this.axios.get(url); | ||
this.checkPermissions(res); | ||
return res.data | ||
}; | ||
/** | ||
* Get all resources of a collection. | ||
* | ||
* @param {string} type - The JSON:API Resource type. | ||
* @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. | ||
* | ||
* @returns {object[]} An array of JSON:API collections. | ||
* | ||
* @example @lang js | ||
* const collections = await this.$druxt.getCollectionAll('node--recipe', 'fields[node--recipe]=title') | ||
*/ | ||
DruxtClient.prototype.getCollectionAll = async function getCollectionAll (type, query) { | ||
var collections = []; | ||
var res = await this.getCollection(type, query); | ||
collections.push(res); | ||
while (((res.links || {}).next || {}).href) { | ||
query = res.links.next.href.split('?')[1]; | ||
res = await this.getCollection(type, query); | ||
async getCollectionAll(type, query) { | ||
const collections = []; | ||
let res = await this.getCollection(type, query); | ||
collections.push(res); | ||
} | ||
return collections | ||
}; | ||
/** | ||
* Get index of all available resources, or the optionally specified resource. | ||
* | ||
* @example @lang js | ||
* const { href } = await this.$druxt.getIndex('node--article') | ||
* | ||
* @param {string} resource - (Optional) A specific resource to query. | ||
* | ||
* @returns {object} The resource index object or the specified resource. | ||
*/ | ||
DruxtClient.prototype.getIndex = async function getIndex (resource) { | ||
if (this.index && !resource) { | ||
return this.index | ||
} | ||
if (this.index && resource) { | ||
return this.index[resource] ? this.index[resource] : false | ||
} | ||
var index = await this.axios.get(this.options.endpoint); | ||
this.index = index.data.links; | ||
// Use JSON API resource config to decorate the index. | ||
if (this.index[this.options.jsonapiResourceConfig]) { | ||
var resources = await this.axios.get(this.index[this.options.jsonapiResourceConfig].href); | ||
for (var resourceType in resources.data.data) { | ||
var resource$1 = resources.data.data[resourceType]; | ||
var internal = resource$1.attributes.drupal_internal__id.split('--'); | ||
var item = { | ||
resourceType: resource$1.attributes.resourceType, | ||
entityType: internal[0], | ||
bundle: internal[1], | ||
resourceFields: resource$1.attributes.resourceFields | ||
}; | ||
var id = [item.entityType, item.bundle].join('--'); | ||
this.index[id] = Object.assign({}, item, | ||
this.index[id]); | ||
while (((res.links || {}).next || {}).href) { | ||
query = res.links.next.href.split("?")[1]; | ||
res = await this.getCollection(type, query); | ||
collections.push(res); | ||
} | ||
return collections; | ||
} | ||
if (resource) { | ||
return this.index[resource] ? this.index[resource] : false | ||
} | ||
return this.index | ||
}; | ||
/** | ||
* Get a JSON:API resource by type and ID. | ||
* | ||
* @example @lang js | ||
* const data = await this.$druxt.getResource('node--article', id) | ||
* | ||
* @param {string} type - The JSON:API Resource type. | ||
* @param {string} id - The Drupal resource UUID. | ||
* @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. | ||
* | ||
* @returns {object} The JSON:API resource data. | ||
*/ | ||
DruxtClient.prototype.getResource = async function getResource (type, id, query) { | ||
if (!id || !type) { | ||
return false | ||
} | ||
var ref = await this.getIndex(type); | ||
var href = ref.href; | ||
if (!href) { | ||
href = this.options.endpoint + '/' + type.replace('--', '/'); | ||
} | ||
var url = this.buildQueryUrl((href + "/" + id), query); | ||
try { | ||
var resource = await this.axios.get(url); | ||
return resource.data | ||
} catch (e) { | ||
return false | ||
} | ||
}; | ||
/** | ||
* DruxtClient options object. | ||
* | ||
* @typedef {object} DruxtClientOptions | ||
* | ||
* @param {object} [axios] - Axios instance settings. | ||
* @param {string} [endpoint=jsonapi] - The JSON:API endpoint. | ||
* @param {string} [jsonapiResourceConfig=jsonapi_resource_config--jsonapi_resource_config] - | ||
* The JSON:API resource config type, used for [JSON:API Extras](https://www.drupal.org/project/jsonapi_extras) support. | ||
* | ||
* @see {@link https://github.com/axios/axios#request-config} | ||
* | ||
* @example @lang js | ||
* { | ||
* axios: { | ||
* headers: { 'X-Custom-Header': true }, | ||
* }, | ||
* endpoint: 'api', | ||
* } | ||
*/ | ||
/** | ||
* @typedef {string|object} DruxtClientQuery | ||
* | ||
* A correctly formatted JSON:API query string or object. | ||
* | ||
* @example | ||
* page[limit]=5&page[offset]=5 | ||
* | ||
* @example @lang js | ||
* new DrupalJsonApiParams().addPageLimit(5) | ||
* | ||
* @see {@link https://www.npmjs.com/package/drupal-jsonapi-params} | ||
*//** | ||
* The DruxtModule base Vue.js component. | ||
* | ||
* Extend this component to build a Druxt module. | ||
* | ||
* @example @lang js | ||
* import { DruxtModule } from 'druxt' | ||
* export default { | ||
* name: 'DruxtTestModule', | ||
* extends: DruxtModule, | ||
* druxt: { | ||
* componentOptions: () => ([['wrapper']]), | ||
* propsData: (ctx) => ({ | ||
* bar: ctx.bar, | ||
* foo: ctx.foo, | ||
* }), | ||
* } | ||
* } | ||
*/ | ||
var script$2 = { | ||
components: { DruxtWrapper: __vue_component__$1 }, | ||
/** */ | ||
props: { | ||
/** | ||
* The module value. | ||
* | ||
* @type {(Array|Boolean|Date|Number|Object|String)} | ||
* @model | ||
*/ | ||
value: { | ||
type: [Array, Boolean, Date, Number, Object, String], | ||
default: null, | ||
}, | ||
wrapper: { | ||
type: Object, | ||
default: function () { return ({ | ||
component: 'div', | ||
propsData: {}, | ||
}); } | ||
}, | ||
}, | ||
/** | ||
* Loads the Druxt module data and applies a wrapper component as required. | ||
* | ||
* **Important:** If your component has an existing `fetch` method, you must manually invoke | ||
* the `DruxtModule.fetch()` hook. | ||
* | ||
* @example @lang js <caption>Manually invoking DruxtModule.fetch().</caption> | ||
* import { DruxtModule } from 'druxt' | ||
* export default { | ||
* name: 'DruxtTestModule', | ||
* extends: DruxtModule, | ||
* async fetch() { | ||
* await DruxtModule.fetch.call(this) | ||
* } | ||
* druxt: { | ||
* componentOptions: () => ([['wrapper']]), | ||
* propsData: (ctx) => ({ | ||
* bar: ctx.bar, | ||
* foo: ctx.foo, | ||
* }), | ||
* } | ||
* } | ||
*/ | ||
fetch: async function fetch() { | ||
if (!(this.$options || {}).druxt) { | ||
return false | ||
async getIndex(resource) { | ||
if (this.index && !resource) { | ||
return this.index; | ||
} | ||
// Build wrapper component object. | ||
var options = this.getModuleComponents(); | ||
var component = { | ||
is: (((options.filter(function (o) { return o.global; }) || [])[0] || {}).name || 'DruxtWrapper'), | ||
options: options.map(function (o) { return o.name; }) || [], | ||
}; | ||
// Get scoped slots. | ||
component.slots = Object.keys(this.getScopedSlots()); | ||
// Get wrapper data. | ||
var wrapperData = await this.getWrapperData(component.is); | ||
component.settings = wrapperData.druxt || {}; | ||
// Build wrapper component propsData. | ||
component = Object.assign({}, component, this.getModulePropsData(wrapperData.props)); | ||
// Set component data. | ||
this.component = component; | ||
}, | ||
/** | ||
* @property {ComponentData} component - The wrapper component and propsData to be rendered. | ||
* @property {object} model - The model object. | ||
*/ | ||
data: function (ref) { | ||
var value = ref.value; | ||
return ({ | ||
component: { | ||
$attrs: {}, | ||
is: 'DruxtWrapper', | ||
options: [], | ||
props: {}, | ||
propsData: {}, | ||
settings: {}, | ||
slots: [], | ||
}, | ||
model: value, | ||
}); | ||
}, | ||
/** */ | ||
methods: { | ||
/** | ||
* Get list of module wrapper components. | ||
* | ||
* @returns {Components} | ||
*/ | ||
getModuleComponents: function getModuleComponents() { | ||
var this$1 = this; | ||
if (!(this.$options.druxt || {}).componentOptions) { | ||
return [] | ||
} | ||
var options = this.$options.druxt.componentOptions.call(this, this); | ||
if (!options || !options.length) { | ||
return [] | ||
} | ||
// Build list of available components. | ||
var components = []; | ||
var loop = function () { | ||
var variants = []; | ||
components = components.concat( set.map(function (item) { | ||
// Build array of name parts. | ||
var parts = variants.length ? [].concat( variants[0].parts ) : []; | ||
parts.push(scule.pascalCase(scule.splitByCase(item))); | ||
// Convert parts into a pascalCase component name. | ||
var name = scule.pascalCase([this$1.$options.name ].concat( parts)); | ||
// Check if component is globally registered. | ||
var global = !!this$1.$options.components[name]; | ||
// Store set variant data to be used in next set item. | ||
variants.unshift({ global: global, name: name, parts: parts }); | ||
return { global: global, name: name, parts: parts } | ||
})); | ||
}; | ||
for (var set of options.filter(function (set) { return Array.isArray(set); })) loop(); | ||
// Filter unique components. | ||
var unique = components.filter((function (s) { return function (o) { return !s.has(o.name) && s.add(o.name); }; })(new Set)); | ||
// Sort items by parts length. | ||
var sorted = unique.sort(function (a, b) { return b.parts.length - a.parts.length; }); | ||
return sorted | ||
}, | ||
/** | ||
* Get module propsData via modules `druxt.propsData()` callback. | ||
* | ||
* @example @lang js | ||
* { | ||
* bar: 'foo', | ||
* foo: 'bar', | ||
* } | ||
* | ||
* @return {object} | ||
*/ | ||
getModulePropsData: function getModulePropsData(wrapperProps) { | ||
if ( wrapperProps === void 0 ) wrapperProps = {}; | ||
if (!(this.$options.druxt || {}).propsData) { | ||
return {} | ||
} | ||
var propsData = this.$options.druxt.propsData.call(this, this); | ||
// Props. | ||
var props = {}; | ||
var propsKeys = Object.keys(wrapperProps).filter(function (i) { return Object.keys(propsData).includes(i); }); | ||
for (var key of propsKeys) { | ||
props[key] = propsData[key]; | ||
} | ||
// $attrs. | ||
var $attrs = Object.assign({}, this.$attrs); | ||
delete $attrs['data-fetch-key']; | ||
var $attrsKeys = Object.keys(propsData).filter(function (i) { return !Object.keys(wrapperProps).includes(i); }); | ||
for (var key$1 of $attrsKeys) { | ||
$attrs[key$1] = propsData[key$1]; | ||
} | ||
return { $attrs: $attrs, props: props, propsData: propsData } | ||
}, | ||
/** | ||
* Gets a Druxt modules scoped slots, and if there's no default slots, | ||
* provides a develop mode debug default or passes through to a | ||
* default template. | ||
* | ||
* @return {object} | ||
*/ | ||
getScopedSlots: function getScopedSlots() { | ||
var this$1 = this; | ||
var h = this.$createElement; | ||
var scopedSlots = typeof (this.$options.druxt || {}).slots === 'function' | ||
? this.$options.druxt.slots.call(this, h) | ||
: {}; | ||
// Pass through default scoped slot if provided. | ||
if (typeof this.$scopedSlots.default === 'function') { | ||
scopedSlots.default = function (attrs) { return this$1.$scopedSlots.default(Object.assign({}, ((this$1.$options.druxt || {}).propsData || (function () {}))(this$1), | ||
attrs)); }; | ||
} | ||
// Provide debug data if Nuxt is running in dev mode. | ||
if (!scopedSlots.default && this.$nuxt.context.isDev) { | ||
scopedSlots.default = function () { return h( | ||
'details', | ||
{ | ||
style: { | ||
border: '2px dashed lightgrey', | ||
margin: '0.5em 0', | ||
padding: '1em', | ||
}, | ||
}, | ||
[ | ||
h('summary', [("[" + (this$1.$options._componentTag) + "] Missing default slot.")]), | ||
h('label', ['Component options:', h('ul', this$1.component.options.map(function (s) { return h('li', [s]); }))]), | ||
h('br'), | ||
h('label', ['propsData:', h('pre', [h('code', [JSON.stringify(this$1.component.propsData, null, '\t')])])]) | ||
] | ||
); }; | ||
} | ||
return scopedSlots | ||
}, | ||
/** | ||
* Get wrapper component data. | ||
* | ||
* @param {string} component - The Wrapper component name. | ||
* | ||
* @return {WrapperData} | ||
*/ | ||
getWrapperData: async function getWrapperData(component) { | ||
var wrapperData = { druxt: {}, props: {} }; | ||
if (!this.$options.components[component]) { | ||
return wrapperData | ||
} | ||
// Get data from resolved component. | ||
if (this.$options.components[component].options) { | ||
wrapperData = this.$options.components[component].options; | ||
} | ||
// Get data from unresolved component. | ||
else if (typeof this.$options.components[component] === 'function' && this._init) { | ||
wrapperData = (await this.$options.components[component].call(this)) || {}; | ||
} | ||
var options = Vue.util.mergeOptions({}, wrapperData); | ||
return { | ||
druxt: options.druxt || {}, | ||
props: options.props || {}, | ||
} | ||
if (this.index && resource) { | ||
return this.index[resource] ? this.index[resource] : false; | ||
} | ||
}, | ||
watch: { | ||
model: function model() { | ||
if (this.component.props.value !== this.model) { | ||
this.component.props.value = this.model; | ||
// Only emit 'input' if using the default 'DruxtWrapper' component. | ||
if (this.component.is === 'DruxtWrapper') { | ||
this.$emit('input', this.model); | ||
} | ||
const index = await this.axios.get(this.options.endpoint); | ||
this.index = index.data.links; | ||
if (this.index[this.options.jsonapiResourceConfig]) { | ||
const resources = await this.axios.get(this.index[this.options.jsonapiResourceConfig].href); | ||
for (const resourceType in resources.data.data) { | ||
const resource2 = resources.data.data[resourceType]; | ||
const internal = resource2.attributes.drupal_internal__id.split("--"); | ||
const item = { | ||
resourceType: resource2.attributes.resourceType, | ||
entityType: internal[0], | ||
bundle: internal[1], | ||
resourceFields: resource2.attributes.resourceFields | ||
}; | ||
const id = [item.entityType, item.bundle].join("--"); | ||
this.index[id] = { | ||
...item, | ||
...this.index[id] | ||
}; | ||
} | ||
}, | ||
value: function value() { | ||
if (this.value !== this.model) { | ||
this.model = this.value; | ||
} | ||
} | ||
}, | ||
render: function render(h) { | ||
var self = this; | ||
var wrapperData = { | ||
class: this.wrapper.class || undefined, | ||
style: this.wrapper.style || undefined, | ||
props: this.wrapper.propsData, | ||
}; | ||
// Return only wrapper if fetch state is still pending. | ||
if (this.$fetchState.pending) { | ||
return h(this.wrapper.component, wrapperData) | ||
if (resource) { | ||
return this.index[resource] ? this.index[resource] : false; | ||
} | ||
// Return wrapped component. | ||
var attrs = Object.assign({}, this.component.$attrs, this.$attrs); | ||
delete attrs['data-fetch-key']; | ||
return h(this.wrapper.component, wrapperData, [ | ||
h(this.component.is, { | ||
attrs: attrs, | ||
on: { | ||
input: function input(value) { | ||
self.model = value; | ||
self.$emit('input', value); | ||
} | ||
}, | ||
props: this.component.props, | ||
ref: 'component', | ||
scopedSlots: this.getScopedSlots(), | ||
}) | ||
]) | ||
return this.index; | ||
} | ||
}; | ||
async getResource(type, id, query) { | ||
if (!id || !type) { | ||
return false; | ||
} | ||
let { href } = await this.getIndex(type); | ||
if (!href) { | ||
href = this.options.endpoint + "/" + type.replace("--", "/"); | ||
} | ||
const url = this.buildQueryUrl(`${href}/${id}`, query); | ||
try { | ||
const resource = await this.axios.get(url); | ||
return resource.data; | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
} | ||
/** | ||
* @typedef {object[]} Components | ||
* @property {boolean} global - Component global registration state. | ||
* @property {string} name - The component name. | ||
* @property {string[]} parts - The component naming parts. | ||
* | ||
* @example @lang js | ||
* [{ | ||
* global: true, | ||
* pascal: 'DruxtTestModuleWrapper', | ||
* parts: ['Wrapper'], | ||
* }] | ||
*/ | ||
/** | ||
* @typedef {object} ComponentData | ||
* @property {object} $attrs - propsData not registered by the Wrapper component. | ||
* @property {string} is=DruxtWrapper - The Wrapper component name. | ||
* @property {string[]} options - The Wrapper component options. | ||
* @property {object} props - propsData registered by the Wrapper component. | ||
* @property {object} propsData - The component propsData object. | ||
* @property {object} settings - Druxt settings object provided by the Wrapper component. | ||
* | ||
* @example @lang js | ||
* { | ||
* $attrs: { bar: 'foo' }, | ||
* is: 'DruxtTestModuleWrapper', | ||
* options: [ | ||
* 'DruxtTestModuleWrapper', | ||
* ], | ||
* props: { foo: 'bar' }, | ||
* propsData: { | ||
* bar: 'foo', | ||
* foo: 'bar', | ||
* }, | ||
* settings: { fooBar: true }, | ||
* } | ||
*/ | ||
/** | ||
* @typedef {object} WrapperData | ||
* @property {object} druxt - Druxt settings object for use by Druxt module. | ||
* @property {object} props - Registered props oject. | ||
* | ||
* @example @lang js | ||
* { | ||
* druxt: { fooBar: true }, | ||
* props: { | ||
* foo: { | ||
* type: String, | ||
* default: '', | ||
* } | ||
* } | ||
* } | ||
*//* script */ | ||
var __vue_script__$2 = script$2; | ||
/* template */ | ||
/* style */ | ||
var __vue_inject_styles__$2 = undefined; | ||
/* scoped */ | ||
var __vue_scope_id__$2 = undefined; | ||
/* module identifier */ | ||
var __vue_module_identifier__$2 = "data-v-f605eeaa"; | ||
/* functional template */ | ||
var __vue_is_functional_template__$2 = undefined; | ||
/* style inject */ | ||
/* style inject SSR */ | ||
/* style inject shadow dom */ | ||
var __vue_component__$2 = /*#__PURE__*/normalizeComponent( | ||
{}, | ||
__vue_inject_styles__$2, | ||
__vue_script__$2, | ||
__vue_scope_id__$2, | ||
__vue_is_functional_template__$2, | ||
__vue_module_identifier__$2, | ||
false, | ||
undefined, | ||
undefined, | ||
undefined | ||
);var getDrupalJsonApiParams = function (query) { | ||
const getDrupalJsonApiParams = (query) => { | ||
return new drupalJsonapiParams.DrupalJsonApiParams().initialize(query); | ||
};var dehydrateResources = function (ref) { | ||
var commit = ref.commit; | ||
var queryObject = ref.queryObject; | ||
var resources = ref.resources; | ||
}; | ||
return resources.map(function (data) { | ||
// Generate a query link for included resources. | ||
// This is used to determine if the resource is a partial. | ||
var link = decodeURI(((data.links || {}).self || {}).href || ''); | ||
var href = typeof (queryObject.fields || {})[data.type] === 'string' | ||
? [link.split('?')[0], ("fields[" + (data.type) + "]=" + (queryObject.fields[data.type]))].join('?') | ||
: link; | ||
// Commit the included resource. | ||
commit('druxt/addResource', { | ||
const dehydrateResources = ({ commit, queryObject, resources }) => { | ||
return resources.map((data) => { | ||
const link = decodeURI(((data.links || {}).self || {}).href || ""); | ||
const href = typeof (queryObject.fields || {})[data.type] === "string" ? [link.split("?")[0], `fields[${data.type}]=${queryObject.fields[data.type]}`].join("?") : link; | ||
commit("druxt/addResource", { | ||
resource: { | ||
data: data, | ||
links: { self: { href: href } }, | ||
data, | ||
links: { self: { href } } | ||
} | ||
}); | ||
return { id: data.id, type: data.type } | ||
}) | ||
return { id: data.id, type: data.type }; | ||
}); | ||
}; | ||
var DruxtStore = function (ref) { | ||
var store = ref.store; | ||
if (typeof store === 'undefined') { | ||
throw new TypeError('Vuex store not found.') | ||
const DruxtStore = ({ store }) => { | ||
if (typeof store === "undefined") { | ||
throw new TypeError("Vuex store not found."); | ||
} | ||
/** | ||
* @namespace | ||
*/ | ||
var namespace = 'druxt'; | ||
/** | ||
* The DruxtStore Vuex module. | ||
* | ||
* Provides a Vuex state object, mutations and actions for interacting with the DruxtClient. | ||
* | ||
* @name druxt | ||
* @module druxt | ||
*/ | ||
var module = { | ||
const namespace = "druxt"; | ||
const module = { | ||
namespaced: true, | ||
/** | ||
* Vuex State object. | ||
* | ||
* @name state | ||
* @type {object} | ||
* @property {DruxtClientCollections} collections - JSON:API resource collections store. | ||
* @property {object} resources - JSON:API resources store. | ||
* @readonly | ||
*/ | ||
state: function () { return ({ | ||
state: () => ({ | ||
collections: {}, | ||
resources: {} | ||
}); }, | ||
/** | ||
* Vuex Mutations. | ||
*/ | ||
}), | ||
mutations: { | ||
/** | ||
* @name addCollection | ||
* @mutator {object} addCollection=collections Adds a JSON:API collection to the Vuex state object. | ||
* @param {addCollectionContext} context | ||
* | ||
* @example @lang js | ||
* this.$store.commit('druxt/addCollection', { collection, type, hash }) | ||
*/ | ||
addCollection: function addCollection (state, ref) { | ||
var collection = ref.collection; | ||
var type = ref.type; | ||
var hash = ref.hash; | ||
if (!state.collections[type]) { Vue.set(state.collections, type, {}); } | ||
// Parse the query. | ||
var link = decodeURI((((collection || {}).links || {}).self || {}).href || ''); | ||
var query = link.split('?')[1] || ''; | ||
var queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
// Store and dehydrate collection resources. | ||
collection.data = dehydrateResources({ commit: this.commit, queryObject: queryObject, resources: collection.data }); | ||
// Extract and store included resources. | ||
addCollection(state, { collection, type, hash }) { | ||
if (!state.collections[type]) | ||
Vue__default['default'].set(state.collections, type, {}); | ||
const link = decodeURI((((collection || {}).links || {}).self || {}).href || ""); | ||
const query = link.split("?")[1] || ""; | ||
const queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
collection.data = dehydrateResources({ commit: this.commit, queryObject, resources: collection.data }); | ||
if (collection.included) { | ||
collection.included = dehydrateResources({ commit: this.commit, queryObject: queryObject, resources: collection.included }); | ||
collection.included = dehydrateResources({ commit: this.commit, queryObject, resources: collection.included }); | ||
delete collection.included; | ||
} | ||
// Recursively merge new collection data into stored collection. | ||
collection = merge(state.collections[type][hash] || {}, collection, { arrayMerge: function (dst, src) { return src; } }); | ||
Vue.set(state.collections[type], hash, collection); | ||
collection = merge__default['default'](state.collections[type][hash] || {}, collection, { arrayMerge: (dst, src) => src }); | ||
Vue__default['default'].set(state.collections[type], hash, collection); | ||
}, | ||
/** | ||
* @name addResource | ||
* @mutator {object} addResource=resources Adds a JSON:API resource to the Vuex state object. | ||
* @param {addResourceContext} context | ||
* | ||
* @example @lang js | ||
* this.$store.commit('druxt/addResource', { resource }) | ||
*/ | ||
addResource: function addResource (state, ref) { | ||
var resource = ref.resource; | ||
var hash = ref.hash; | ||
addResource(state, { resource, hash }) { | ||
if (hash) { | ||
console.warn('[druxt] The `hash` argument for `druxt/addResource` has been deprecated, see https://druxtjs.org/guide/deprecations.html#druxtstore-addresource-hash'); | ||
console.warn("[druxt] The `hash` argument for `druxt/addResource` has been deprecated, see https://druxtjs.org/guide/deprecations.html#druxtstore-addresource-hash"); | ||
} | ||
var ref$1 = (resource || {}).data || {}; | ||
var id = ref$1.id; | ||
var type = ref$1.type; | ||
const { id, type } = (resource || {}).data || {}; | ||
if (!id || !type) { | ||
// @TODO - Error? | ||
return | ||
return; | ||
} | ||
// Parse the query. | ||
var link = decodeURI((((resource || {}).links || {}).self || {}).href || ''); | ||
var query = link.split('?')[1] || ''; | ||
var queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
// Add cache flag to resource. | ||
var flag = typeof (queryObject.fields || {})[((resource || {}).data || {}).type] === 'string' ? '_druxt_partial' : '_druxt_full'; | ||
const link = decodeURI((((resource || {}).links || {}).self || {}).href || ""); | ||
const query = link.split("?")[1] || ""; | ||
const queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
const flag = typeof (queryObject.fields || {})[((resource || {}).data || {}).type] === "string" ? "_druxt_partial" : "_druxt_full"; | ||
resource[flag] = Date.now(); | ||
// Ensure Resource type array is reactive. | ||
if (!state.resources[type]) { Vue.set(state.resources, type, {}); } | ||
// Extract and store included data. | ||
if (!state.resources[type]) | ||
Vue__default['default'].set(state.resources, type, {}); | ||
if (resource.included) { | ||
dehydrateResources({ commit: this.commit, queryObject: queryObject, resources: resource.included }); | ||
dehydrateResources({ commit: this.commit, queryObject, resources: resource.included }); | ||
delete resource.included; | ||
} | ||
// Recursively merge new resource data into stored resource. | ||
resource = merge(state.resources[type][id] || {}, resource, { arrayMerge: function (dst, src) { return src; } }); | ||
Vue.set(state.resources[type], id, resource); | ||
}, | ||
resource = merge__default['default'](state.resources[type][id] || {}, resource, { arrayMerge: (dst, src) => src }); | ||
Vue__default['default'].set(state.resources[type], id, resource); | ||
} | ||
}, | ||
/** | ||
* Vuex Actions. | ||
*/ | ||
actions: { | ||
/** | ||
* Get collection of resources. | ||
* | ||
* @name getCollection | ||
* @action getCollection | ||
* @param {getCollectionContext} context | ||
* @return {object[]} Array of Drupal JSON:API resource data. | ||
* | ||
* @example @lang js | ||
* // Load all currently published Articles. | ||
* const resources = await this.$store.dispatch('druxt/getCollection', { | ||
* type: 'node--article', | ||
* query: new DrupalJsonApiParams().addFilter('status', '1'), | ||
* }) | ||
*/ | ||
getCollection: async function getCollection (ref, ref$1) { | ||
var commit = ref.commit; | ||
var state = ref.state; | ||
var type = ref$1.type; | ||
var query = ref$1.query; | ||
// Generate a hash using query data excluding the 'fields' and 'include' data. | ||
var queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
var hash = query ? md5(JSON.stringify(Object.assign({}, queryObject, {fields: {}, include: []}))) : '_default'; | ||
// If collection hash exists, re-hydrate and return the data. | ||
async getCollection({ commit, state }, { type, query }) { | ||
const queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
const hash = query ? md5__default['default'](JSON.stringify({ ...queryObject, fields: {}, include: [] })) : "_default"; | ||
if ((state.collections[type] || {})[hash]) { | ||
return Object.assign({}, state.collections[type][hash], | ||
// Hydrate resource data. | ||
{data: state.collections[type][hash].data.map(function (o) { return (state.resources[o.type][o.id] || {}).data; })}) | ||
return { | ||
...state.collections[type][hash], | ||
data: state.collections[type][hash].data.map((o) => (state.resources[o.type][o.id] || {}).data) | ||
}; | ||
} | ||
// Get the collection using the DruxtClient instance. | ||
var collection = await this.$druxt.getCollection(type, query); | ||
// Store the collection in the DruxtStore. | ||
commit('addCollection', { collection: Object.assign({}, collection), type: type, hash: hash }); | ||
return collection | ||
const collection = await this.$druxt.getCollection(type, query); | ||
commit("addCollection", { collection: { ...collection }, type, hash }); | ||
return collection; | ||
}, | ||
/** | ||
* Get JSON:API Resource. | ||
* | ||
* - Executes query against Drupal JSON:API. | ||
* - Caches result in the Vuex store. | ||
* - Returns cached result from Vuex store when available. | ||
* | ||
* @name getResource | ||
* @action getResource=resources | ||
* @param {getResourceContext} context | ||
* @return {object} The Drupal JSON:API resource. | ||
* | ||
* @example @lang js | ||
* const resource = await this.$store.dispatch('druxt/getResource', { type: 'node--article', id }) | ||
*/ | ||
getResource: async function getResource (ref, ref$1) { | ||
var commit = ref.commit; | ||
var dispatch = ref.dispatch; | ||
var state = ref.state; | ||
var type = ref$1.type; | ||
var id = ref$1.id; | ||
var query = ref$1.query; | ||
// Get the resource from the store if it's avaialble. | ||
var storedResource = (state.resources[type] || {})[id] ? | ||
Object.assign({}, state.resources[type][id]) | ||
: null; | ||
// Parse the query. | ||
var queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
queryObject.include = Array.isArray(queryObject.include) | ||
? queryObject.include.join(',') | ||
: queryObject.include; | ||
// Ensure that includes are in the fields filter. | ||
if (queryObject.include && typeof (queryObject.fields || {})[type] === 'string') { | ||
var fields$1 = queryObject.fields[type].split(',').filter(function (s) { return s; }); | ||
var includes = queryObject.include.split(',').filter(function (s) { return s && !s.includes('.'); }); | ||
queryObject.fields[type] = Array.from( | ||
new Set(fields$1.concat( includes)) | ||
).filter(function (s) { return s; }).join(','); | ||
async getResource({ commit, dispatch, state }, { type, id, query }) { | ||
const storedResource = (state.resources[type] || {})[id] ? { ...state.resources[type][id] } : null; | ||
const queryObject = getDrupalJsonApiParams(query).getQueryObject(); | ||
queryObject.include = Array.isArray(queryObject.include) ? queryObject.include.join(",") : queryObject.include; | ||
if (queryObject.include && typeof (queryObject.fields || {})[type] === "string") { | ||
const fields2 = queryObject.fields[type].split(",").filter((s) => s); | ||
const includes = queryObject.include.split(",").filter((s) => s && !s.includes(".")); | ||
queryObject.fields[type] = Array.from(new Set([...fields2, ...includes])).filter((s) => s).join(","); | ||
} | ||
// Hydrate included data based on the include query. | ||
var included = []; | ||
let included = []; | ||
if (queryObject.include && storedResource) { | ||
// Request included resources from druxt/getResource. | ||
var resources = | ||
await Promise.all(queryObject.include.split(',') | ||
.filter(function (s) { return Object.keys((storedResource.data.relationships || {})).includes(s); }) | ||
.map(function (key) { | ||
var ref = storedResource.data.relationships[key]; | ||
var data = ref.data; | ||
data = Array.isArray(data) ? data : [data]; | ||
// Get any sub-includes, e.g., `media,media.image` becomes `image`. | ||
var include = queryObject.include.split(',') | ||
.filter(function (s) { return s.startsWith((key + ".")); }) | ||
.map(function (s) { return s.slice(key.length + 1); }) | ||
.join(','); | ||
return data.filter(function (o) { return typeof o === 'object' && o; }).map(function (o) { | ||
return dispatch('getResource', { | ||
type: o.type, | ||
id: o.id, | ||
query: Object.assign({}, queryObject, {include: include}), | ||
}) | ||
}) | ||
}) | ||
.flat() | ||
); | ||
// Merge all nested, included resources. | ||
for (var include of resources) { | ||
included = included.concat( [include.data], include.included || []); | ||
const resources = await Promise.all(queryObject.include.split(",").filter((s) => Object.keys(storedResource.data.relationships || {}).includes(s)).map((key) => { | ||
let { data } = storedResource.data.relationships[key]; | ||
data = Array.isArray(data) ? data : [data]; | ||
const include = queryObject.include.split(",").filter((s) => s.startsWith(`${key}.`)).map((s) => s.slice(key.length + 1)).join(","); | ||
return data.filter((o) => typeof o === "object" && o).map((o) => { | ||
return dispatch("getResource", { | ||
type: o.type, | ||
id: o.id, | ||
query: { ...queryObject, include } | ||
}); | ||
}); | ||
}).flat()); | ||
for (const include of resources) { | ||
included = [...included, include.data, ...include.included || []]; | ||
} | ||
storedResource.included = included; | ||
} | ||
// Return if we have the full resource. | ||
if ((storedResource || {})._druxt_full) { | ||
return storedResource | ||
return storedResource; | ||
} | ||
var isFull = typeof (queryObject.fields || {})[type] !== 'string'; | ||
// Determine if we have all the requested field data. | ||
var fields = isFull ? true : (queryObject.fields || {})[type]; | ||
const isFull = typeof (queryObject.fields || {})[type] !== "string"; | ||
let fields = isFull ? true : (queryObject.fields || {})[type]; | ||
if (storedResource && !isFull && fields) { | ||
var queryFields = fields.split(','); | ||
var resourceFields = Object.keys(((storedResource || {}).data || {}).attributes || {}).concat( Object.keys(((storedResource || {}).data || {}).relationships || {}) ); | ||
var missingFields = queryFields.filter(function (key) { return !resourceFields.includes(key); }); | ||
const queryFields = fields.split(","); | ||
const resourceFields = [ | ||
...Object.keys(((storedResource || {}).data || {}).attributes || {}), | ||
...Object.keys(((storedResource || {}).data || {}).relationships || {}) | ||
]; | ||
const missingFields = queryFields.filter((key) => !resourceFields.includes(key)); | ||
fields = !!missingFields.length; | ||
// Modify query to load additional fields, if required. | ||
queryObject.fields[type] = (missingFields || []).join(',') || undefined; | ||
queryObject.fields[type] = (missingFields || []).join(",") || void 0; | ||
} | ||
// Request the resource from the DruxtClient if required. | ||
var resource; | ||
let resource; | ||
if (!storedResource || fields) { | ||
resource = await this.$druxt.getResource(type, id, getDrupalJsonApiParams(queryObject)); | ||
commit('addResource', { resource: Object.assign({}, resource) }); | ||
commit("addResource", { resource: { ...resource } }); | ||
} | ||
// Build resource to be returned. | ||
var result = Object.assign({}, (state.resources[type] || {})[id]); | ||
// Merge included resources into resource. | ||
const result = { ...(state.resources[type] || {})[id] }; | ||
if (queryObject.include && ((resource || {}).included || (storedResource || {}).included)) { | ||
included = ( (resource || {}).included || [] ).concat( (storedResource || {}).included || [] ); | ||
result.included = Array.from(new Set(included.filter(function (o) { return (o || {}).id; }).map(function (o) { return o.id; }))) | ||
.map(function (id) { return included.find(function (o) { return o.id === id; }); }); | ||
included = [ | ||
...(resource || {}).included || [], | ||
...(storedResource || {}).included || [] | ||
]; | ||
result.included = Array.from(new Set(included.filter((o) => (o || {}).id).map((o) => o.id))).map((id2) => included.find((o) => o.id === id2)); | ||
} | ||
return result | ||
}, | ||
return result; | ||
} | ||
} | ||
}; | ||
store.registerModule(namespace, module, { | ||
@@ -1397,253 +313,56 @@ preserveState: Boolean(store.state[namespace]) | ||
/** | ||
* Parameters for the `addCollection` mutation. | ||
* | ||
* @typedef {object} addCollectionContext | ||
* | ||
* @param {object} collection - A collection of JSON:API resources. | ||
* @param {string} type - The JSON:API collection resource type. | ||
* @param {string} hash - An md5 hash of the query string. | ||
* | ||
* @example @lang js | ||
* { | ||
* collection: { | ||
* jsonapi: {}, | ||
* data: [{}], | ||
* links: {} | ||
* }, | ||
* type: 'node--page', | ||
* hash: '_default' | ||
* } | ||
*/ | ||
/** | ||
* Parameters for the `addResource` mutation. | ||
* | ||
* @typedef {object} addResourceContext | ||
* | ||
* @param {object} resource - The JSON:API resource. | ||
* | ||
* @example @lang js | ||
* { | ||
* resource: { | ||
* jsonapi: {}, | ||
* data: {}, | ||
* links: {} | ||
* }, | ||
* } | ||
*/ | ||
/** | ||
* Parameters for the `getCollection` action. | ||
* | ||
* @typedef {object} getCollectionContext | ||
* | ||
* @param {string} type - The JSON:API collection resource type. | ||
* @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. | ||
* | ||
* @example @lang js | ||
* { | ||
* type: 'node--page', | ||
* query: new DrupalJsonApiParams().addFilter('status', '1') | ||
* } | ||
*/ | ||
/** | ||
* Parameters for the `getResource` action. | ||
* | ||
* @typedef {object} getResourceContext | ||
* | ||
* @param {string} type - The JSON:API Resource type. | ||
* @param {string} id - The Drupal resource UUID. | ||
* @param {DruxtClientQuery} [query] - A correctly formatted JSON:API query string or object. | ||
* | ||
* @example @lang js | ||
* { | ||
* type: 'node--page', | ||
* id: 'd8dfd355-7f2f-4fc3-a149-288e4e293bdd' | ||
* } | ||
*/ | ||
/** | ||
* @typedef {string|object} DruxtClientQuery | ||
* | ||
* A correctly formatted JSON:API query string or object. | ||
* | ||
* @example | ||
* page[limit]=5&page[offset]=5 | ||
* | ||
* @example @lang js | ||
* new DrupalJsonApiParams().addPageLimit(5) | ||
* | ||
* @see {@link https://www.npmjs.com/package/drupal-jsonapi-params} | ||
*//** | ||
* Druxt utility class. | ||
* | ||
* @deprecated | ||
* @private | ||
*/ | ||
var DruxtClass = function DruxtClass() {}; | ||
/** | ||
* Get component data from available options. | ||
* | ||
* @param {object} vm - The DruxtJS module Vue.js component. | ||
* @param {string[]} options - The component naming options. | ||
* @param {boolean} [all=false] - Returns all options if true, else only globally registered options. | ||
* @param {string} [prefix] - A string to prefix all components. | ||
* | ||
* @returns {WrapperComponents} | ||
*/ | ||
DruxtClass.prototype.getComponents = function getComponents (vm, options, all, prefix) { | ||
if ( all === void 0 ) all = false; | ||
var results = []; | ||
var unique = {}; | ||
options | ||
// Filter out incorrectly typed items. | ||
.filter(function (item) { return Array.isArray(item); }) | ||
// Process each item. | ||
.map(function (item) { | ||
var variants = []; | ||
item.map(function (string) { | ||
var parts = variants.length ? [].concat( variants[0].parts ) : []; | ||
class DruxtClass { | ||
constructor() { | ||
} | ||
getComponents(vm, options, all = false, prefix) { | ||
const results = []; | ||
const unique = {}; | ||
options.filter((item) => Array.isArray(item)).map((item) => { | ||
const variants = []; | ||
item.map((string) => { | ||
const parts = variants.length ? [...variants[0].parts] : []; | ||
parts.push(string); | ||
var clone = [].concat( parts ); | ||
// Attach prefix as required. | ||
if (typeof prefix !== 'string' && (prefix !== false || typeof prefix === 'undefined') && ((vm || {}).$options || {}).name) { | ||
prefix = vm.$options.name.match(/[A-Z][a-z]+/g).map(function (word) { return word.toLowerCase(); }).join('-'); | ||
const clone = [...parts]; | ||
if (typeof prefix !== "string" && (prefix !== false || typeof prefix === "undefined") && ((vm || {}).$options || {}).name) { | ||
prefix = vm.$options.name.match(/[A-Z][a-z]+/g).map((word) => word.toLowerCase()).join("-"); | ||
} | ||
if (prefix) { | ||
clone.unshift(prefix); | ||
} | ||
// Generate component name values. | ||
var kebab = clone.map(function (string) { return string.toLowerCase().replace(/--|_/g, '-'); }).join('-'); | ||
var pascal = kebab.replace(/((\b|[^a-zA-Z0-9]+)[a-zA-Z0-9])/gi, function (match, p1, p2) { return match.toUpperCase().replace(p2, ''); }); | ||
// Check if component is globally registered. | ||
var global = false; | ||
for (var name of [kebab, pascal]) { | ||
if (typeof (((vm || {}).$options || {}).components || {})[name] !== 'undefined') { | ||
const kebab = clone.map((string2) => string2.toLowerCase().replace(/--|_/g, "-")).join("-"); | ||
const pascal = kebab.replace(/((\b|[^a-zA-Z0-9]+)[a-zA-Z0-9])/gi, (match, p1, p2) => match.toUpperCase().replace(p2, "")); | ||
let global = false; | ||
for (const name of [kebab, pascal]) { | ||
if (typeof (((vm || {}).$options || {}).components || {})[name] !== "undefined") { | ||
global = true; | ||
break | ||
break; | ||
} | ||
} | ||
variants.unshift({ global: global, kebab: kebab, parts: parts, pascal: pascal, prefix: prefix }); | ||
variants.unshift({ global, kebab, parts, pascal, prefix }); | ||
}); | ||
// Add variants to results. | ||
variants.map(function (variant) { | ||
// Ensure unique results. | ||
variants.map((variant) => { | ||
if (unique[variant.pascal]) { | ||
return | ||
return; | ||
} | ||
unique[variant.pascal] = true; | ||
results.push(variant); | ||
}); | ||
}); | ||
// Return globally registered components or all. | ||
return results | ||
.filter(function (option) { return option.global || !!all; }) | ||
.sort(function (a, b) { return b.parts.length - a.parts.length; }) | ||
}; | ||
/** | ||
* Get the Druxt module data from the referenced component. | ||
* | ||
* @returns {ModuleData} | ||
*/ | ||
DruxtClass.prototype.getModuleData = async function getModuleData (vm) { | ||
if (typeof ((vm || {}).$options || {}).druxt !== 'function') { | ||
return false | ||
return results.filter((option) => option.global || !!all).sort((a, b) => b.parts.length - a.parts.length); | ||
} | ||
var moduleData = await vm.$options.druxt({ vm: vm }); | ||
if ((vm.$options || {}).name) { | ||
moduleData.name = vm.$options.name.match(/[A-Z][a-z]+/g).map(function (word) { return word.toLowerCase(); }).join('-'); | ||
} | ||
return moduleData | ||
};/** | ||
* @name DruxtComponentMixin | ||
* @deprecated | ||
* @private | ||
*/ | ||
var DruxtComponentMixin = { | ||
components: { DruxtWrapper: __vue_component__$1 }, | ||
/** | ||
* @property {Component} component - The wrapper component and propsData to be rendered. | ||
*/ | ||
data: function () { return ({ | ||
component: { | ||
is: 'DruxtWrapper', | ||
options: [], | ||
propsData: {}, | ||
}, | ||
}); }, | ||
props: { | ||
wrapper: { | ||
type: Object, | ||
default: function () { return ({ | ||
component: 'div', | ||
propsData: {}, | ||
}); } | ||
async getModuleData(vm) { | ||
if (typeof ((vm || {}).$options || {}).druxt !== "function") { | ||
return false; | ||
} | ||
}, | ||
/** | ||
* The Nuxt Fetch hook. | ||
* | ||
* Loads the Druxt module data and applies a wrapper component as required. | ||
* | ||
* **Important:** If your component has an existing `fetch` method, you must manually invoke | ||
* the `DruxtComponentMixin.fetch()` hook. | ||
* | ||
* @see {@link https://nuxtjs.org/api/pages-fetch/} | ||
* | ||
* @example @lang vue <caption>Manually invoking DruxtComponentMixin.fetch().</caption> | ||
* <script> | ||
* import { DruxtComponentMixin } from 'druxt' | ||
* export default { | ||
* mixins: [DruxtComponentMixin], | ||
* | ||
* async fetch { | ||
* await DruxtComponentMixin.fetch.call(this) | ||
* } | ||
* } | ||
* </script> | ||
*/ | ||
fetch: async function fetch() { | ||
console.warn('DruxtComponentMixin is deprecated in favour of DruxtModule.'); | ||
// @todo - check for this.$druxt plugin. | ||
var druxt = new DruxtClass(); | ||
var moduleData = await druxt.getModuleData(this); | ||
this.component.propsData = moduleData.propsData || {}; | ||
if (!moduleData.componentOptions) { | ||
return | ||
const moduleData = await vm.$options.druxt({ vm }); | ||
if ((vm.$options || {}).name) { | ||
moduleData.name = vm.$options.name.match(/[A-Z][a-z]+/g).map((word) => word.toLowerCase()).join("-"); | ||
} | ||
return moduleData; | ||
} | ||
} | ||
var options = druxt.getComponents(this, moduleData.componentOptions, true); | ||
this.component.options = options.map(function (item) { return item.pascal; }); | ||
var available = options.filter(function (item) { return item.global; }); | ||
if (!available.length) { | ||
return | ||
} | ||
this.component.is = available[0].pascal; | ||
}, | ||
};exports.Druxt=__vue_component__;exports.DruxtClass=DruxtClass;exports.DruxtClient=DruxtClient;exports.DruxtComponentMixin=DruxtComponentMixin;exports.DruxtModule=__vue_component__$2;exports.DruxtStore=DruxtStore;exports.DruxtWrapper=__vue_component__$1;exports.default=DruxtNuxtModule; | ||
exports.DruxtClass = DruxtClass; | ||
exports.DruxtClient = DruxtClient; | ||
exports.DruxtStore = DruxtStore; | ||
exports['default'] = DruxtNuxtModule; |
import Vue from 'vue' | ||
import { Druxt, DruxtClient, DruxtWrapper } from 'druxt' | ||
import { DruxtClient } from 'druxt' | ||
import Druxt from 'druxt/dist/components/Druxt.vue' | ||
import DruxtWrapper from 'druxt/dist/components/DruxtWrapper.vue' | ||
// Install the Druxt Vue.js component. | ||
@@ -5,0 +8,0 @@ Vue.use({ |
{ | ||
"name": "druxt", | ||
"version": "0.8.3", | ||
"description": "A Bridge between frameworks, Nuxt.js in the front, Drupal in the back.", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/druxt/druxt.js" | ||
}, | ||
"version": "0.9.0", | ||
"description": "The Fully Decoupled Drupal Framework for Nuxt.js.", | ||
"keywords": [ | ||
@@ -21,2 +17,11 @@ "cms", | ||
], | ||
"homepage": "https://druxtjs.org", | ||
"bugs": { | ||
"url": "https://github.com/druxt/druxt.js/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/druxt/druxt.js" | ||
}, | ||
"license": "MIT", | ||
"author": { | ||
@@ -27,10 +32,11 @@ "name": "Stuart Clark", | ||
}, | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/druxt/druxt.js/issues" | ||
"exports": { | ||
".": { | ||
"require": "./dist/druxt.ssr.js", | ||
"import": "./dist/druxt.esm.js" | ||
}, | ||
"./components/*": "./dist/components/*" | ||
}, | ||
"homepage": "https://druxtjs.org", | ||
"main": "dist/druxt.ssr.js", | ||
"module": "dist/druxt.esm.js", | ||
"unpkg": "dist/druxt.min.js", | ||
"files": [ | ||
@@ -40,22 +46,4 @@ "dist", | ||
], | ||
"scripts": { | ||
"build": "cross-env NODE_ENV=production rollup -c", | ||
"build:es": "cross-env NODE_ENV=production rollup -c --format es", | ||
"build:ssr": "cross-env NODE_ENV=production rollup -c --format cjs", | ||
"build:unpkg": "cross-env NODE_ENV=production rollup -c --format iife", | ||
"dev": "nodemon --ext js,vue --exec 'npm run lint && npm run build && npm run test'", | ||
"docs:dev": "npx druxt-docgen -c vuepress.config dev", | ||
"docs:build": "npx druxt-docgen -c vuepress.config build", | ||
"lint": "eslint --ext .js,.vue src", | ||
"start": "npm run dev", | ||
"test": "jest" | ||
}, | ||
"nodemonConfig": { | ||
"ignore": [ | ||
"dist/*" | ||
] | ||
}, | ||
"dependencies": { | ||
"axios": "^0.21.1", | ||
"codecov": "^3.8.2", | ||
"deepmerge": "^4.2.2", | ||
@@ -65,49 +53,12 @@ "drupal-jsonapi-params": "^1.2.1", | ||
"querystring": "^0.2.0", | ||
"scule": "^0.1.1", | ||
"vuex": "^3.6.0" | ||
"scule": "^0.2.0" | ||
}, | ||
"devDependencies": { | ||
"@babel/plugin-transform-runtime": "^7.12.10", | ||
"@babel/preset-env": "^7.12.11", | ||
"@rollup/plugin-alias": "^2.2.0", | ||
"@rollup/plugin-buble": "^0.20.0", | ||
"@rollup/plugin-replace": "^2.3.4", | ||
"@vue/test-utils": "^1.1.2", | ||
"babel-core": "^7.0.0-bridge.0", | ||
"babel-eslint": "^10.1.0", | ||
"bootstrap-vue": "^2.21.2", | ||
"cross-env": "^6.0.3", | ||
"druxt-docgen": "^0.4.0", | ||
"eslint": "^6.7.2", | ||
"eslint-plugin-vue": "^6.2.2", | ||
"jest": "^25.1.0", | ||
"jest-junit": "^11.1.0", | ||
"jest-mock-axios": "^3.2.0", | ||
"minimist": "^1.2.0", | ||
"nodemon": "^2.0.7", | ||
"rollup": "^1.26.3", | ||
"rollup-plugin-commonjs": "^10.1.0", | ||
"rollup-plugin-terser": "^7.0.2", | ||
"rollup-plugin-vue": "^5.1.6", | ||
"vue": "^2.6.12", | ||
"vue-jest": "^3.0.7", | ||
"vue-template-compiler": "^2.6.12", | ||
"vue-test-utils": "^1.0.0-beta.11" | ||
"optionalDependencies": { | ||
"core-js": "^3.16.1", | ||
"vue": "^2.6.14", | ||
"vuex": "^3.6.2" | ||
}, | ||
"engines": { | ||
"node": ">=8" | ||
}, | ||
"jest": { | ||
"coverageDirectory": "./coverage/", | ||
"collectCoverage": true, | ||
"moduleFileExtensions": [ | ||
"js", | ||
"json", | ||
"vue" | ||
], | ||
"transform": { | ||
".*\\.(vue)$": "vue-jest", | ||
"^.+\\.js$": "<rootDir>/node_modules/babel-jest" | ||
} | ||
"publishConfig": { | ||
"access": "public" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
0
10
1
50044
9
792
2
0
1
+ Addedcore-js@3.38.1(transitive)
+ Addedscule@0.2.1(transitive)
- Removedcodecov@^3.8.2
- Removedvuex@^3.6.0
- Removed@tootallnate/once@1.1.2(transitive)
- Removedagent-base@6.0.2(transitive)
- Removedargparse@1.0.10(transitive)
- Removedargv@0.0.2(transitive)
- Removedbalanced-match@1.0.2(transitive)
- Removedbrace-expansion@1.1.11(transitive)
- Removedcodecov@3.8.3(transitive)
- Removedconcat-map@0.0.1(transitive)
- Removeddebug@4.3.7(transitive)
- Removedesprima@4.0.1(transitive)
- Removedfast-url-parser@1.1.3(transitive)
- Removedhttp-proxy-agent@4.0.1(transitive)
- Removedhttps-proxy-agent@5.0.1(transitive)
- Removedignore-walk@3.0.4(transitive)
- Removedjs-yaml@3.14.1(transitive)
- Removedminimatch@3.1.2(transitive)
- Removedms@2.1.3(transitive)
- Removednode-fetch@2.7.0(transitive)
- Removedpunycode@1.4.1(transitive)
- Removedscule@0.1.1(transitive)
- Removedsprintf-js@1.0.3(transitive)
- Removedstream-events@1.0.5(transitive)
- Removedstubs@3.0.0(transitive)
- Removedteeny-request@7.1.1(transitive)
- Removedtr46@0.0.3(transitive)
- Removedurlgrey@1.0.0(transitive)
- Removeduuid@8.3.2(transitive)
- Removedwebidl-conversions@3.0.1(transitive)
- Removedwhatwg-url@5.0.0(transitive)
Updatedscule@^0.2.0