Skip to content

Commit

Permalink
chore(#337): added initial nuxt 3 support for $druxt plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Decipher committed Nov 21, 2024
1 parent 34ada4d commit 3f23e2b
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 87 deletions.
14 changes: 10 additions & 4 deletions packages/druxt/src/client.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import axios from 'axios'
import { stringify } from 'querystring'
import consola from 'consola'
// import { useLogger } from '@nuxt/kit'
import { consola } from 'consola'

/**
* The Druxt JSON:API client.
Expand All @@ -23,7 +24,7 @@ class DruxtClient {
constructor(baseUrl, options = {}) {
// Consola logger.
this.log = consola.create({ defaults: {
tag: 'DruxtClient'
tag: 'druxt'
}})

// Check for URL.
Expand Down Expand Up @@ -351,14 +352,18 @@ class DruxtClient {
* @returns {object} The resource index object or the specified resource.
*/
async getIndex(resource, prefix) {
if ((this.index || {})[prefix] && !resource) {
// If the index is already loaded and there's no resource specified, return.
if (this.index?.[prefix] && !resource) {
return this.index[prefix]
}

if ((this.index || {})[prefix] && resource) {
// If the index is already loaded and there's a resource specified, return
// the resource.
if (this.index?.[prefix] && resource) {
return this.index[prefix][resource] ? this.index[prefix][resource] : false
}

// Fetch the JSON:API index data.
const url = [prefix, this.options.endpoint].join('')
const { data } = await this.get(url)
let index = data.links
Expand Down Expand Up @@ -410,6 +415,7 @@ class DruxtClient {
// Set index.
this.index[prefix] = index

// Return specific resource or all resources from the index.
return resource ? this.index[prefix][resource] || false : this.index[prefix]
}

Expand Down
192 changes: 110 additions & 82 deletions packages/druxt/src/nuxt/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { addPluginTemplate, addTemplate, defineNuxtModule, installModule } from '@nuxt/kit'
import { addPluginTemplate, addTemplate, defineNuxtModule, extendRouteRules, installModule, isNuxt2 } from '@nuxt/kit'
import chalk from 'chalk'
import { DrupalJsonApiParams } from 'drupal-jsonapi-params'
import { join, normalize, resolve } from 'path'
Expand Down Expand Up @@ -55,30 +55,30 @@ const DruxtNuxtModule = defineNuxtModule({
nuxt.options.baseUrl = options.baseUrl = options.baseUrl.endsWith('/') ? options.baseUrl.slice(0, -1) : options.baseUrl
nuxt.options.endpoint = options.endpoint = options.endpoint.startsWith('/') ? options.endpoint : `/${options.endpoint}`

const druxt = new DruxtClient(options.baseUrl, {
...options,
// Disable API Proxy, as Proxies aren't available at build.
proxy: { ...options.proxy || {}, api: false },
})

// Nuxt proxy integration.
// Proxy integration.
if (options.proxy) {
const proxies = {}

// Enable proxying of the API endpoint.
// This is primarily used to avoid CORS errors.
if (options.proxy?.api) {
const druxt = new DruxtClient(options.baseUrl, {
...options,
// Disable API Proxy, as Proxies aren't available at build.
proxy: { ...options.proxy, api: false },
})

// Main API Endpoint.
proxies[options.endpoint] = options.baseUrl

// Langcode prefixed API endpoints.
const languageResourceType = 'configurable_language--configurable_language'
if (((await druxt.getIndex(languageResourceType)) || {}).href) {
if ((await druxt.getIndex(languageResourceType))?.href) {
const query = new DrupalJsonApiParams().addFields(languageResourceType, ['drupal_internal__id'])
const languages = (await druxt.getCollectionAll(languageResourceType, query) || [])
.map((o) => o.data)
.flat()
.filter((o) => !['und', 'zxx'].includes(((o || {}).attributes || {}).drupal_internal__id))
.filter((o) => !['und', 'zxx'].includes(o?.attributes?.drupal_internal__id))
.map((o) => o.attributes.drupal_internal__id)
for (const langcode of languages) {
proxies[`/${langcode}${options.endpoint}`] = options.baseUrl
Expand All @@ -95,109 +95,137 @@ const DruxtNuxtModule = defineNuxtModule({
proxies[`/sites/${filesPath}/files`] = options.baseUrl
}

// If there are existing proxy settings, merge in the appropriate format.
if (nuxt.options.proxy) {
if (Array.isArray(nuxt.options.proxy)) {
nuxt.options.proxy = [
...nuxt.options.proxy,
...Object.keys(proxies).map((path) => `${options.baseUrl}${path}`)
]
// If Nuxt2, configure and install the @nuxtjs/proxy module.
if (isNuxt2()) {
// If there are existing proxy settings, merge in the appropriate format.
if (nuxt.options.proxy) {
if (Array.isArray(nuxt.options.proxy)) {
nuxt.options.proxy = [
...this.options.proxy,
...Object.keys(proxies).map((path) => `${options.baseUrl}${path}`)
]
}
else {
nuxt.options.proxy = {
...nuxt.options.proxy,
...proxies
}
}
}
// Otherwise just set the the required proxies.
else {
nuxt.options.proxy = {
...nuxt.options.proxy,
...proxies
}
nuxt.options.proxy = proxies
}
}
// Otherwise just set the the required proxies.
else {
nuxt.options.proxy = proxies
}

// Enable the Proxy module.
await installModule('@nuxtjs/proxy', {}, nuxt)
}
// Enable the Proxy module.
await installModule('@nuxtjs/proxy', {}, nuxt)
}

// Install the @nuxtjs/axios module.
if (!options.axios) {
nuxt.options.axios = {
baseURL: options.baseUrl,
proxy: !!options.proxy?.api,
...nuxt.options.axios
// Otherwise use the Nuxt 3 Nitro Route Rules.
// @TODO - Confirm this actually works.
else {
Object.keys(proxies).forEach((route) => extendRouteRules(`${route}/**`, { proxy: `${options.baseUrl}${route}/**` }))
}
await installModule('@nuxtjs/axios', {}, nuxt)
}

// Register components directories.
nuxt.hook('components:dirs', dirs => {
dirs.push({ path: join(__dirname, '../dist/components') })
dirs.push({
path: resolve(__dirname, '../dist/components'),
global: true
})
})

// Add plugin.
// Add $druxt plugin.
addPluginTemplate({
src: resolve(__dirname, '../templates/plugin.js'),
fileName: 'druxt.js',
options
options: {
...options,
isNuxt2
}
})

// Make the $axios and $druxt plugins the first to load.
const extendPlugins = nuxt.options.extendPlugins
nuxt.options.extendPlugins = (plugins) => {
// Run the user defined extendPlugins function if defined.
plugins = typeof extendPlugins === 'function' ? extendPlugins(plugins) : plugins
// Install the @nuxtjs/axios module for Nuxt 2.
if (isNuxt2()) {
if (!options.axios) {
nuxt.options.axios = {
baseURL: options.baseUrl,
proxy: !!options.proxy?.api,
...nuxt.options.axios
}
await installModule('@nuxtjs/axios', {}, nuxt)
}

// Make the $axios and $druxt plugins the first to load.
const extendPlugins = nuxt.options.extendPlugins
nuxt.options.extendPlugins = (plugins) => {
// Run the user defined extendPlugins function if defined.
plugins = typeof extendPlugins === 'function' ? extendPlugins(plugins) : plugins

// Extract the $axios plugin.
const axiosIndex = plugins.findIndex(({ src }) => src === normalize(`${nuxt.options.buildDir}/axios.js`))
const axiosPlugin = plugins[axiosIndex]
plugins.splice(axiosIndex, 1)
// Extract the $axios plugin.
const axiosIndex = plugins.findIndex(({ src }) => src === normalize(`${nuxt.options.buildDir}/axios.js`))
if (axiosIndex === -1) throw new Error('The $axios plugin is missing.')
const axiosPlugin = plugins[axiosIndex]
plugins.splice(axiosIndex, 1)

// Extract the $druxt plugin.
const druxtIndex = plugins.findIndex(({ src }) => src === normalize(`${nuxt.options.buildDir}/druxt.js`))
const druxtPlugin = plugins[druxtIndex]
plugins.splice(druxtIndex, 1)
// Extract the $druxt plugin.
const druxtIndex = plugins.findIndex(({ src }) => src === normalize(`${nuxt.options.buildDir}/druxt.js`))
// if (druxtIndex === -1) throw new Error('The $druxt plugin is missing.')
const druxtPlugin = plugins[druxtIndex]
plugins.splice(druxtIndex, 1)

// Re-add the plugins.
plugins = [axiosPlugin, druxtPlugin, ...plugins]
// Re-add the plugins.
plugins = [axiosPlugin, druxtPlugin, ...plugins]

return plugins.filter((o) => o)
}
return plugins.filter((o) => o)
}

// Add Vuex plugin.
addPluginTemplate({
src: resolve(__dirname, '../templates/store.js'),
fileName: 'store/druxt.js',
options
})
// Enable Vuex Store.
nuxt.options.store = true

// Enable components auto-discovery by default.
nuxt.options.components = nuxt.options.components ?? true

// Enable Vuex Store.
nuxt.options.store = true
// Add Vuex plugin.
addPluginTemplate({
src: resolve(__dirname, '../templates/store.js'),
fileName: 'store/druxt.js',
options
})
}

// Enable components auto-discovery by default.
nuxt.options.components = nuxt.options.components ?? true

// Get the version from the root directories package.json.
const rootDir = __dirname.endsWith('/src/nuxt') ? '../..' : '..'
const version = require(join(__dirname, `${rootDir}/package.json`)).version

// Add CLI badge.
nuxt.options.cli.badgeMessages.push(`${chalk.blue.bold('Druxt')} @ v${version}`)
nuxt.options.cli.badgeMessages.push(`${chalk.bold('API:')} ${chalk.blue.underline(options.baseUrl + options.endpoint)}`)
// Add Druxt module info to CLI.
if (isNuxt2()) {
nuxt.options.cli.badgeMessages.push(`${chalk.blue.bold('Druxt')} @ v${version}`)
nuxt.options.cli.badgeMessages.push(`${chalk.bold('API:')} ${chalk.blue.underline(options.baseUrl + options.endpoint)}`)
}
else {
// @TODO: Chalk seems to not work in Nuxt 3.
console.log(`Druxt ${version}`)
console.log(` \u279C API: ${options.baseUrl + options.endpoint}\n`)
}

// Development mode features.
if (nuxt.options.dev) {
// Add the template stubber server middleware.
nuxt.options.serverMiddleware.push({
path: '/_druxt/template',
handler: 'druxt/dist/server-middleware/template.mjs'
})

// Add the Vue devtools plugin.
addPluginTemplate({
src: resolve(__dirname, '../dist/plugins/devtools.mjs'),
fileName: 'druxt-devtools.js'
})
}
// @TODO - Development mode features need to be re-added.
// if (nuxt.options.dev) {
// // Add the template stubber server middleware.
// nuxt.options.serverMiddleware.push({
// path: '/_druxt/template',
// handler: 'druxt/dist/server-middleware/template.mjs'
// })

// // Add the Vue devtools plugin.
// addPluginTemplate({
// src: resolve(__dirname, '../dist/plugins/devtools.mjs'),
// fileName: 'druxt-devtools.js'
// })
// }

// Nuxt Storybook.
nuxt.hook('storybook:config', async ({ stories }) => {
Expand Down
16 changes: 15 additions & 1 deletion packages/druxt/templates/plugin.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { DruxtClient } from 'druxt'

<% const { isNuxt2 } = options; delete options.isNuxt2 %>
<% if (typeof isNuxt2 === 'function' && isNuxt2()) { %>
// Druxt Plugin for Nuxt 2.
export default ({ app }, inject) => {
<% } else { %>
// Druxt Plugin for Nuxt 3.
export default defineNuxtPlugin((app) => {
<% } %>
const options = <%= JSON.stringify(options) %>

// Disable the proxy for server side requests.
if (!process.client && (options.proxy || {}).api) {
if (!process.client && options.proxy?.api) {
options.proxy.api = false
}

Expand All @@ -19,5 +26,12 @@ export default ({ app }, inject) => {

const druxt = new DruxtClient(options.baseUrl, options)
druxt.settings = options
<% if (typeof isNuxt2 === 'function' && isNuxt2()) { %>
inject('druxt', druxt)
}
<% } else { %>
return {
provide: { druxt }
}
})
<% } %>

0 comments on commit 3f23e2b

Please sign in to comment.