diff --git a/package-lock.json b/package-lock.json index 65c347895..297b33ac8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@vueuse/core": "^13.6.0" }, "devDependencies": { - "@nuxt/icon": "^1.15.0", + "@nuxt/icon": "^2.0.0", "@nuxt/image": "1.11.0", "@nuxtjs/i18n": "^9.5.2", "@nuxtjs/tailwindcss": "^6.14.0", @@ -64,9 +64,9 @@ } }, "node_modules/@antfu/utils": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", - "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-9.2.0.tgz", + "integrity": "sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw==", "dev": true, "license": "MIT", "funding": { @@ -3296,19 +3296,19 @@ "license": "MIT" }, "node_modules/@iconify/utils": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz", - "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.0.1.tgz", + "integrity": "sha512-A78CUEnFGX8I/WlILxJCuIJXloL0j/OJ9PSchPAfCargEIKmUBWvvEMmKWB5oONwiUqlNt+5eRufdkLxeHIWYw==", "dev": true, "license": "MIT", "dependencies": { - "@antfu/install-pkg": "^1.0.0", - "@antfu/utils": "^8.1.0", + "@antfu/install-pkg": "^1.1.0", + "@antfu/utils": "^9.2.0", "@iconify/types": "^2.0.0", - "debug": "^4.4.0", - "globals": "^15.14.0", + "debug": "^4.4.1", + "globals": "^15.15.0", "kolorist": "^1.8.0", - "local-pkg": "^1.0.0", + "local-pkg": "^1.1.1", "mlly": "^1.7.4" } }, @@ -4846,28 +4846,61 @@ } }, "node_modules/@nuxt/icon": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@nuxt/icon/-/icon-1.15.0.tgz", - "integrity": "sha512-kA0rxqr1B601zNJNcOXera8CyYcxUCEcT7dXEC7rwAz71PRCN5emf7G656eKEQgtqrD4JSj6NQqWDgrmFcf/GQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@nuxt/icon/-/icon-2.0.0.tgz", + "integrity": "sha512-sy8+zkKMYp+H09S0cuTteL3zPTmktqzYPpPXV9ZkLNjrQsaPH08n7s/9wjr+C/K/w2R3u18E3+P1VIQi3xaq1A==", "dev": true, "license": "MIT", "dependencies": { - "@iconify/collections": "^1.0.563", + "@iconify/collections": "^1.0.579", "@iconify/types": "^2.0.0", - "@iconify/utils": "^2.3.0", + "@iconify/utils": "^3.0.0", "@iconify/vue": "^5.0.0", - "@nuxt/devtools-kit": "^2.5.0", - "@nuxt/kit": "^3.17.5", + "@nuxt/devtools-kit": "^2.6.2", + "@nuxt/kit": "^4.0.3", "consola": "^3.4.2", "local-pkg": "^1.1.1", "mlly": "^1.7.4", "ohash": "^2.0.11", "pathe": "^2.0.3", - "picomatch": "^4.0.2", + "picomatch": "^4.0.3", "std-env": "^3.9.0", "tinyglobby": "^0.2.14" } }, + "node_modules/@nuxt/icon/node_modules/@nuxt/kit": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.0.3.tgz", + "integrity": "sha512-9+lwvP4n8KhO91azoebO0o39smESGzEV4HU6nef9HIFyt04YwlVMY37Pk63GgZn0WhWVjyPWcQWs0rUdZUYcPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "c12": "^3.2.0", + "consola": "^3.4.2", + "defu": "^6.1.4", + "destr": "^2.0.5", + "errx": "^0.1.0", + "exsolve": "^1.0.7", + "ignore": "^7.0.5", + "jiti": "^2.5.1", + "klona": "^2.0.6", + "mlly": "^1.7.4", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "pkg-types": "^2.2.0", + "scule": "^1.3.0", + "semver": "^7.7.2", + "std-env": "^3.9.0", + "tinyglobby": "^0.2.14", + "ufo": "^1.6.1", + "unctx": "^2.4.1", + "unimport": "^5.2.0", + "untyped": "^2.0.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, "node_modules/@nuxt/image": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/@nuxt/image/-/image-1.11.0.tgz", diff --git a/package.json b/package.json index 0739b3ce7..c9cc08776 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@vueuse/core": "^13.6.0" }, "devDependencies": { - "@nuxt/icon": "^1.15.0", + "@nuxt/icon": "^2.0.0", "@nuxt/image": "1.11.0", "@nuxtjs/i18n": "^9.5.2", "@nuxtjs/tailwindcss": "^6.14.0", diff --git a/woonuxt_base/app/app.config.ts b/woonuxt_base/app/app.config.ts index 15043a2fd..8247c9b28 100644 --- a/woonuxt_base/app/app.config.ts +++ b/woonuxt_base/app/app.config.ts @@ -1,27 +1,8 @@ +import { defaultConfig } from '../modules/woonuxt-bridge/defaults'; + /** * App configuration. * This file is used to configure the app settings. * Below are the default values. */ -export default defineAppConfig({ - siteName: 'WooNuxt', - shortDescription: 'This is an example of a WooNuxt store. It provides a modern, fast, and SEO friendly ecommerce store built with Nuxt and WooCommerce.', - description: `WooNuxt is unmatched when it comes to performance and scalability. Reap the benefits of having a online store that out performs all of your competitors. You can edit components to display your own information just like the one you're reading now.`, - baseUrl: 'https://v3.woonuxt.com', - siteImage: 'https://user-images.githubusercontent.com/5116925/218879668-f4c1f9fd-bef4-44b0-bc7f-e87d994aa3a1.png', - storeSettings: { - autoOpenCart: false, - showReviews: true, - showFilters: true, - showOrderByDropdown: true, - showSKU: true, - showRelatedProducts: true, - showProductCategoriesOnSingleProduct: true, - showBreadcrumbOnSingleProduct: true, - showMoveToWishlist: true, - hideBillingAddressForVirtualProducts: false, - initStoreOnUserActionToReduceServerLoad: true, - saleBadge: 'percent', // 'percent', 'onSale' or 'hidden' - socialLoginsDisplay: 'buttons', // 'buttons' or 'icons' - }, -}); +export default defineAppConfig(defaultConfig); diff --git a/woonuxt_base/app/composables/useFiltering.ts b/woonuxt_base/app/composables/useFiltering.ts index b920efe53..61c9de2f2 100644 --- a/woonuxt_base/app/composables/useFiltering.ts +++ b/woonuxt_base/app/composables/useFiltering.ts @@ -6,7 +6,8 @@ export function useFiltering() { const route = useRoute(); const router = useRouter(); - const runtimeConfig = useRuntimeConfig(); // Declare a variable for the runtime config and the filter and order functions + const appConfig = useAppConfig(); + const globalAttributes = computed(() => appConfig.globalAttributes || []); // Use centralized config instead of runtime config const { updateProductList } = useProducts(); const filterQuery = useState('filter', () => ''); @@ -118,7 +119,7 @@ export function useFiltering() { const ratingCondition = starRating.length ? (product?.averageRating || 0) >= parseFloat(starRating[0] as string) : true; // Product attribute filters - const globalProductAttributes = runtimeConfig?.public?.GLOBAL_PRODUCT_ATTRIBUTES?.map((attribute: any) => attribute.slug) || []; + const globalProductAttributes = (Array.isArray(globalAttributes.value) ? globalAttributes.value : []).map((attribute: any) => attribute.slug); const attributeCondition = globalProductAttributes .map((attribute: string) => { const attributeValues = getFilter(attribute) || []; diff --git a/woonuxt_base/app/composables/useHelpers.ts b/woonuxt_base/app/composables/useHelpers.ts index 40c1c76ca..36b1abedd 100644 --- a/woonuxt_base/app/composables/useHelpers.ts +++ b/woonuxt_base/app/composables/useHelpers.ts @@ -4,12 +4,13 @@ import pkg from '../../../woonuxt_base/package.json'; export function useHelpers() { const route = useRoute(); const runtimeConfig = useRuntimeConfig(); + const appConfig = useAppConfig(); const isShowingMobileMenu = useState('isShowingMobileMenu', () => false); const wooNuxtVersionInfo: string = pkg.version || '0.0.0'; - const productsPerPage: number = runtimeConfig.public?.PRODUCTS_PER_PAGE || 24; - const wooNuxtSEO = Array.isArray(runtimeConfig.public?.WOO_NUXT_SEO) ? runtimeConfig.public?.WOO_NUXT_SEO : []; - const frontEndUrl = runtimeConfig.public?.FRONT_END_URL?.replace(/\/$/, '') || null; + const productsPerPage: number = (appConfig as any).productsPerPage || 24; + const wooNuxtSEO = (appConfig as any).wooNuxtSEO || []; + const frontEndUrl = (appConfig as any).frontEndUrl?.replace(/\/$/, '') || null; const isDev: boolean = process.env.NODE_ENV === 'development'; const FALLBACK_IMG = '/images/placeholder.jpg'; diff --git a/woonuxt_base/app/error.vue b/woonuxt_base/app/error.vue index be17463c0..9a6b19605 100644 --- a/woonuxt_base/app/error.vue +++ b/woonuxt_base/app/error.vue @@ -39,6 +39,8 @@ useSeoMeta({

Error {{ error?.statusCode || '404' }}

{{ error.message }}

+

An unexpected error occurred.

+ Go back to Home
diff --git a/woonuxt_base/modules/woonuxt-bridge.ts b/woonuxt_base/modules/woonuxt-bridge.ts deleted file mode 100644 index 26cab44ea..000000000 --- a/woonuxt_base/modules/woonuxt-bridge.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { defineNuxtModule, useLogger } from '@nuxt/kit'; - -import { $fetch } from 'ofetch'; - -type EnvSpec = { - key: string; - validate?: (v: string) => boolean; - hint?: string; -}; - -const REQUIRED_ENV: EnvSpec[] = [ - { - key: 'GQL_HOST', - hint: 'Format: https://your-site.com/graphql', - }, - { - key: 'NUXT_IMAGE_DOMAINS', - hint: 'Format: domain.com (single domain, comma separated if multiple)', - }, -]; - -function validateEnvironment() { - const errs: string[] = []; - for (const { key, validate, hint } of REQUIRED_ENV) { - const val = process.env[key]?.trim(); - if (!val) { - errs.push(`Missing env: ${key}${hint ? ` (${hint})` : ''}`); - continue; - } - if (validate && !validate(val)) { - errs.push(`Invalid env: ${key}${hint ? ` (${hint})` : ''}`); - } - } - if (errs.length) { - const logger = useLogger('woonuxt-bridge'); - logger.error('\nEnvironment validation failed:\n- ' + errs.join('\n- ')); - logger.error('\nFix your .env (see .env.example) and rerun.\n'); - process.exit(1); - } -} - -const getVersionQuery = `query getVersion { - woonuxtSettings { - wooCommerceSettingsVersion - } -}`; - -// Validate environment variables before module setup -validateEnvironment(); - -export default defineNuxtModule({ - meta: { - name: 'woonuxt-bridge', - configKey: 'woonuxtBridge', - }, - async setup(_, nuxt) { - const logger = useLogger('woonuxt-bridge'); - - // Environment variables are guaranteed to be valid at this point - const GQL_HOST = process.env.GQL_HOST!; - let WOONUXT_SETTINGS_PLUGIN_VERSION = 0; - - // Get the version of the woonuxt-settings plugin - try { - const { data } = await $fetch(GQL_HOST, { - method: 'POST', - body: JSON.stringify({ query: getVersionQuery }), - headers: { Origin: process.env.APP_HOST || 'http://localhost:3000' }, - }); - - const versionString = data.woonuxtSettings?.wooCommerceSettingsVersion || '0.0.0'; - logger.success(`WooNuxt Settings Plugin: v${versionString}`); - - // Convert semantic version to comparable number (e.g., "2.2.1" -> 20201, "1.0.55" -> 10055) - const versionParts = versionString.split('.').map((part: string) => parseInt(part.replace(/\D/g, ''), 10) || 0); - WOONUXT_SETTINGS_PLUGIN_VERSION = versionParts[0] * 10000 + versionParts[1] * 100 + versionParts[2]; - } catch (error) { - logger.error(error); - } - - const wooNuxtSEO = WOONUXT_SETTINGS_PLUGIN_VERSION > 10043 ? 'wooNuxtSEO { provider url handle }' : ''; - const currencyCode = WOONUXT_SETTINGS_PLUGIN_VERSION > 10055 ? 'currencyCode' : ''; - const currencySymbol = WOONUXT_SETTINGS_PLUGIN_VERSION > 10055 ? 'currencySymbol' : ''; - - const woonuxtSettings = `{ - primary_color - logo - publicIntrospectionEnabled - frontEndUrl - productsPerPage - maxPrice - global_attributes { - slug - showCount - openByDefault - label - hideEmpty - } - stripeSettings { - enabled - testmode - test_publishable_key - publishable_key - } - ${wooNuxtSEO} - ${currencyCode} - ${currencySymbol} - }`; - - const query = ` - query getWooNuxtSettings { - woonuxtSettings ${woonuxtSettings} - generalSettings { title } - allSettings { generalSettingsUrl } - }`; - - try { - const { data } = await $fetch(GQL_HOST, { - method: 'POST', - body: JSON.stringify({ query }), - headers: { Origin: process.env.APP_HOST || 'http://localhost:3000' }, - }); - - // Default env variables - process.env.PRIMARY_COLOR = data.woonuxtSettings?.primary_color || '#7F54B2'; - - // Default runtimeConfig - nuxt.options.runtimeConfig.public.LOGO = data.woonuxtSettings?.logo || null; - nuxt.options.runtimeConfig.public.PRODUCTS_PER_PAGE = data.woonuxtSettings?.productsPerPage || process.env.PRODUCTS_PER_PAGE || 24; - nuxt.options.runtimeConfig.public.GLOBAL_PRODUCT_ATTRIBUTES = data.woonuxtSettings?.global_attributes || []; - nuxt.options.runtimeConfig.public.MAX_PRICE = data.woonuxtSettings?.maxPrice || 1000; - nuxt.options.runtimeConfig.public.FRONT_END_URL = data.woonuxtSettings?.frontEndUrl || null; - nuxt.options.runtimeConfig.public.BACKEND_URL = data.allSettings?.generalSettingsUrl || null; - nuxt.options.runtimeConfig.public.CURRENCY_CODE = data.woonuxtSettings?.currencyCode || null; - nuxt.options.runtimeConfig.public.CURRENCY_SYMBOL = data.woonuxtSettings?.currencySymbol || null; - nuxt.options.runtimeConfig.public.WOO_NUXT_SEO = data.woonuxtSettings?.wooNuxtSEO || null; - // Site title - process.env.SITE_TITLE = data.generalSettings?.title || 'WooNuxt'; - - // Stripe - if (data.woonuxtSettings?.stripeSettings?.enabled) { - nuxt.options.runtimeConfig.public.STRIPE_PUBLISHABLE_KEY = - data.woonuxtSettings?.stripeSettings?.testmode === 'yes' - ? data.woonuxtSettings?.stripeSettings?.test_publishable_key - : data.woonuxtSettings?.stripeSettings?.publishable_key; - } - } catch (error) { - logger.error(error); - logger.warn( - 'Error fetching woonuxt settings. Make sure you have the latest version woonuxt-settings plugin installed on WordPress. https://github.com/scottyzen/woonuxt-settings', - ); - } - }, -}); diff --git a/woonuxt_base/modules/woonuxt-bridge/config.ts b/woonuxt_base/modules/woonuxt-bridge/config.ts new file mode 100644 index 000000000..98f9fdc16 --- /dev/null +++ b/woonuxt_base/modules/woonuxt-bridge/config.ts @@ -0,0 +1,47 @@ +import { createResolver } from '@nuxt/kit'; +import { deepMerge } from './utils'; +import { defaultConfig } from './defaults'; + +const { resolve } = createResolver(import.meta.url); + +/** + * Load app.config.ts defaults + */ +export async function loadAppConfig(): Promise { + try { + // For now, use the defaults from our defaults file + // This avoids the issue with defineAppConfig not being available in Node.js context + return defaultConfig; + } catch (error) { + console.warn('Could not load app.config.ts:', error); + return {}; + } +} + +/** + * Apply legacy compatibility mappings to runtime config + */ +export function applyLegacyConfig(nuxt: any, merged: any): void { + // Legacy compatibility - maintain existing runtime config structure + nuxt.options.runtimeConfig.public.LOGO = merged.logo || ''; + nuxt.options.runtimeConfig.public.PRODUCTS_PER_PAGE = merged.productsPerPage || 24; + nuxt.options.runtimeConfig.public.GLOBAL_PRODUCT_ATTRIBUTES = merged.global_attributes || []; + nuxt.options.runtimeConfig.public.MAX_PRICE = merged.maxPrice || 1000; + nuxt.options.runtimeConfig.public.FRONT_END_URL = merged.frontEndUrl || ''; + nuxt.options.runtimeConfig.public.BACKEND_URL = merged.backendUrl || ''; + nuxt.options.runtimeConfig.public.CURRENCY_CODE = merged.currencyCode || ''; + nuxt.options.runtimeConfig.public.CURRENCY_SYMBOL = merged.currencySymbol || ''; + nuxt.options.runtimeConfig.public.WOO_NUXT_SEO = merged.wooNuxtSEO || []; + nuxt.options.runtimeConfig.public.STRIPE_PUBLISHABLE_KEY = merged.stripePublishableKey || ''; + + // Set environment variables for compatibility + process.env.PRIMARY_COLOR = merged.primary_color || '#7F54B2'; + process.env.SITE_TITLE = merged.siteName || 'WooNuxt'; +} + +/** + * Merge app config with WordPress data + */ +export function mergeConfigurations(appConfig: any, wpData: any): any { + return deepMerge(appConfig, wpData); +} diff --git a/woonuxt_base/modules/woonuxt-bridge/defaults.ts b/woonuxt_base/modules/woonuxt-bridge/defaults.ts new file mode 100644 index 000000000..ee6cfbc23 --- /dev/null +++ b/woonuxt_base/modules/woonuxt-bridge/defaults.ts @@ -0,0 +1,26 @@ +/** + * Default configuration values + * This file contains the default configuration that can be imported during build time + */ +export const defaultConfig = { + siteName: 'WooNuxt', + shortDescription: 'This is an example of a WooNuxt store. It provides a modern, fast, and SEO friendly ecommerce store built with Nuxt and WooCommerce.', + description: `WooNuxt is unmatched when it comes to performance and scalability. Reap the benefits of having a online store that out performs all of your competitors. You can edit components to display your own information just like the one you're reading now.`, + baseUrl: 'https://v3.woonuxt.com', + siteImage: 'https://user-images.githubusercontent.com/5116925/218879668-f4c1f9fd-bef4-44b0-bc7f-e87d994aa3a1.png', + storeSettings: { + autoOpenCart: false, + showReviews: true, + showFilters: true, + showOrderByDropdown: true, + showSKU: true, + showRelatedProducts: true, + showProductCategoriesOnSingleProduct: true, + showBreadcrumbOnSingleProduct: true, + showMoveToWishlist: true, + hideBillingAddressForVirtualProducts: false, + initStoreOnUserActionToReduceServerLoad: true, + saleBadge: 'percent', // 'percent', 'onSale' or 'hidden' + socialLoginsDisplay: 'buttons', // 'buttons' or 'icons' + }, +}; diff --git a/woonuxt_base/modules/woonuxt-bridge/index.ts b/woonuxt_base/modules/woonuxt-bridge/index.ts new file mode 100644 index 000000000..5e3a60647 --- /dev/null +++ b/woonuxt_base/modules/woonuxt-bridge/index.ts @@ -0,0 +1,42 @@ +import { defineNuxtModule, useLogger } from '@nuxt/kit'; +import { validateEnvironment, loadAppConfig, fetchWoonuxtSettings, deepMerge, applyLegacyConfig } from './utils'; + +// Validate environment variables before module setup +validateEnvironment(); + +export default defineNuxtModule({ + meta: { + name: 'woonuxt-bridge', + configKey: 'woonuxtBridge', + }, + async setup(_, nuxt) { + const logger = useLogger('woonuxt-config'); + + // 1) Environment variables are guaranteed to be valid at this point + const GQL_HOST = process.env.GQL_HOST!; + + // 2) Load defaults from app.config.ts + const appConfig = await loadAppConfig(); + if (Object.keys(appConfig).length > 0) { + logger.info('Loaded app.config.ts'); + } else { + logger.warn('Could not load app.config.ts'); + } + + // 3) Fetch WordPress settings + logger.info('Fetching WooNuxt settings from WordPress...'); + const wpData = await fetchWoonuxtSettings(GQL_HOST); + + // 4) Merge app config with WordPress data (WordPress data takes precedence) + const merged = deepMerge(appConfig, wpData); + logger.info('Configuration merged successfully'); + + // 5) Update Nuxt app config + nuxt.options.appConfig = merged; + + // 6) Apply legacy compatibility mappings + applyLegacyConfig(nuxt, merged); + + logger.success('WooNuxt configuration setup complete!'); + }, +}); diff --git a/woonuxt_base/modules/woonuxt-bridge/utils.ts b/woonuxt_base/modules/woonuxt-bridge/utils.ts new file mode 100644 index 000000000..fa2594df7 --- /dev/null +++ b/woonuxt_base/modules/woonuxt-bridge/utils.ts @@ -0,0 +1,219 @@ +import { $fetch } from 'ofetch'; +import { createResolver, useLogger } from '@nuxt/kit'; + +const { resolve } = createResolver(import.meta.url); + +/** + * Deep merge two objects recursively + */ +export function deepMerge>(target: T, source: Partial): T { + const result = { ...target }; + + for (const key in source) { + if (source.hasOwnProperty(key)) { + const sourceValue = source[key]; + const targetValue = result[key]; + + if ( + sourceValue && + typeof sourceValue === 'object' && + !Array.isArray(sourceValue) && + targetValue && + typeof targetValue === 'object' && + !Array.isArray(targetValue) + ) { + (result as any)[key] = deepMerge(targetValue, sourceValue); + } else if (sourceValue !== undefined) { + (result as any)[key] = sourceValue; + } + } + } + + return result; +} + +/** + * Environment variable validation + */ +export function validateEnvironment(): void { + const required = { + GQL_HOST: { + validate: (val: string) => val.startsWith('http'), + hint: 'Must be a valid URL starting with http', + }, + }; + + const errs: string[] = []; + for (const [key, { validate, hint }] of Object.entries(required)) { + const val = process.env[key]; + if (!val) { + errs.push(`Missing env: ${key}${hint ? ` (${hint})` : ''}`); + continue; + } + if (validate && !validate(val)) { + errs.push(`Invalid env: ${key}${hint ? ` (${hint})` : ''}`); + } + } + + if (errs.length) { + const logger = useLogger('woonuxt-bridge'); + logger.error('\nEnvironment validation failed:\n- ' + errs.join('\n- ')); + logger.error('\nFix your .env (see .env.example) and rerun.\n'); + process.exit(1); + } +} + +/** + * Load app.config.ts defaults + */ +export async function loadAppConfig(): Promise { + try { + // Try to read the app.config.ts file and extract its default export + const fs = await import('fs/promises'); + const configPath = resolve('../../app/app.config.ts'); + const configContent = await fs.readFile(configPath, 'utf-8'); + + // Extract the configuration object from defineAppConfig call + const configMatch = configContent.match(/defineAppConfig\(\s*(\{[\s\S]*?\})\s*\)/); + if (configMatch) { + // Use Function constructor to safely evaluate the config object + const configObject = new Function('return ' + configMatch[1])(); + return configObject; + } + + return {}; + } catch (error) { + console.warn('Could not load app.config.ts:', error); + return {}; + } +} + +/** + * Fetch WooNuxt settings from WordPress GraphQL endpoint + */ +export async function fetchWoonuxtSettings(gqlHost: string): Promise { + // Check plugin version first + const versionQuery = `query getVersion { + woonuxtSettings { + wooCommerceSettingsVersion + } + }`; + + let pluginVersion = 0; + try { + const { data: versionData } = await $fetch(gqlHost, { + method: 'POST', + body: { + query: versionQuery, + }, + }); + + pluginVersion = versionData?.woonuxtSettings?.wooCommerceSettingsVersion || 0; + } catch (error) { + // Silently fall back to version 0 if version check fails + pluginVersion = 0; + } + + // Build query based on version + let settingsQuery: string; + if (pluginVersion >= 1) { + // Version 1+ supports all fields + settingsQuery = `query getWooNuxtSettings { + woonuxtSettings { + wooCommerceSettingsVersion + siteName + shortDescription + description + siteImage + baseUrl + frontEndUrl + backendUrl + primary_color + storeSettings { + autoOpenCart + showReviews + showFilters + showOrderByDropdown + showSKU + showRelatedProducts + showProductCategoriesOnSingleProduct + showBreadcrumbOnSingleProduct + showMoveToWishlist + hideBillingAddressForVirtualProducts + initStoreOnUserActionToReduceServerLoad + saleBadge + socialLoginsDisplay + } + logo + logo_width + logo_height + logo_mobile + logo_mobile_width + logo_mobile_height + productsPerPage + maxPrice + currencyCode + currencySymbol + global_attributes + wooNuxtSEO { + title + description + ogImage + isEnabled + } + stripePublishableKey + } + }`; + } else { + // Fallback for older versions or if version check fails + settingsQuery = `query getWooNuxtSettings { + woonuxtSettings { + siteName + shortDescription + description + baseUrl + primary_color + logo + productsPerPage + maxPrice + currencyCode + currencySymbol + } + }`; + } + + try { + const { data } = await $fetch(gqlHost, { + method: 'POST', + body: { + query: settingsQuery, + }, + }); + + return data?.woonuxtSettings || {}; + } catch (error) { + console.warn('WordPress endpoint unreachable, using defaults:', error); + return {}; + } +} + +/** + * Apply legacy compatibility mappings to runtime config + */ +export function applyLegacyConfig(nuxt: any, merged: any): void { + // Legacy compatibility - maintain existing runtime config structure + nuxt.options.runtimeConfig.public.LOGO = merged.logo || ''; + nuxt.options.runtimeConfig.public.PRODUCTS_PER_PAGE = merged.productsPerPage || 24; + nuxt.options.runtimeConfig.public.GLOBAL_PRODUCT_ATTRIBUTES = merged.global_attributes || []; + nuxt.options.runtimeConfig.public.MAX_PRICE = merged.maxPrice || 1000; + nuxt.options.runtimeConfig.public.FRONT_END_URL = merged.frontEndUrl || ''; + nuxt.options.runtimeConfig.public.BACKEND_URL = merged.backendUrl || ''; + nuxt.options.runtimeConfig.public.CURRENCY_CODE = merged.currencyCode || ''; + nuxt.options.runtimeConfig.public.CURRENCY_SYMBOL = merged.currencySymbol || ''; + nuxt.options.runtimeConfig.public.WOO_NUXT_SEO = merged.wooNuxtSEO || []; + nuxt.options.runtimeConfig.public.STRIPE_PUBLISHABLE_KEY = merged.stripePublishableKey || ''; + + // Set environment variables for compatibility + process.env.PRIMARY_COLOR = merged.primary_color || '#7F54B2'; + process.env.SITE_TITLE = merged.siteName || 'WooNuxt'; +} \ No newline at end of file diff --git a/woonuxt_base/modules/woonuxt-bridge/wordpress.ts b/woonuxt_base/modules/woonuxt-bridge/wordpress.ts new file mode 100644 index 000000000..904b050db --- /dev/null +++ b/woonuxt_base/modules/woonuxt-bridge/wordpress.ts @@ -0,0 +1,110 @@ +import { $fetch } from 'ofetch'; + +/** + * Fetch WooNuxt settings from WordPress GraphQL endpoint + */ +export async function fetchWoonuxtSettings(gqlHost: string): Promise { + // Check plugin version first + const versionQuery = `query getVersion { + woonuxtSettings { + wooCommerceSettingsVersion + } + }`; + + let pluginVersion = 0; + try { + const { data: versionData } = await $fetch(gqlHost, { + method: 'POST', + body: { + query: versionQuery, + }, + }); + + pluginVersion = versionData?.woonuxtSettings?.wooCommerceSettingsVersion || 0; + } catch (error) { + // Silently fall back to version 0 if version check fails + pluginVersion = 0; + } + + // Build query based on version + let settingsQuery: string; + if (pluginVersion >= 1) { + // Version 1+ supports all fields + settingsQuery = `query getWooNuxtSettings { + woonuxtSettings { + wooCommerceSettingsVersion + siteName + shortDescription + description + siteImage + baseUrl + frontEndUrl + backendUrl + primary_color + storeSettings { + autoOpenCart + showReviews + showFilters + showOrderByDropdown + showSKU + showRelatedProducts + showProductCategoriesOnSingleProduct + showBreadcrumbOnSingleProduct + showMoveToWishlist + hideBillingAddressForVirtualProducts + initStoreOnUserActionToReduceServerLoad + saleBadge + socialLoginsDisplay + } + logo + logo_width + logo_height + logo_mobile + logo_mobile_width + logo_mobile_height + productsPerPage + maxPrice + currencyCode + currencySymbol + global_attributes + wooNuxtSEO { + title + description + ogImage + isEnabled + } + stripePublishableKey + } + }`; + } else { + // Fallback for older versions or if version check fails + settingsQuery = `query getWooNuxtSettings { + woonuxtSettings { + siteName + shortDescription + description + baseUrl + primary_color + logo + productsPerPage + maxPrice + currencyCode + currencySymbol + } + }`; + } + + try { + const { data } = await $fetch(gqlHost, { + method: 'POST', + body: { + query: settingsQuery, + }, + }); + + return data?.woonuxtSettings || {}; + } catch (error) { + console.warn('WordPress endpoint unreachable, using defaults:', error); + return {}; + } +} diff --git a/woonuxt_base/nuxt.config.ts b/woonuxt_base/nuxt.config.ts index e29e560dc..b5562ecca 100644 --- a/woonuxt_base/nuxt.config.ts +++ b/woonuxt_base/nuxt.config.ts @@ -18,7 +18,7 @@ export default defineNuxtConfig({ components: [{ path: resolve('./app/components'), pathPrefix: false }], modules: [ - resolve('./modules/woonuxt-bridge.ts'), + resolve('./modules/woonuxt-bridge'), [ 'nuxt-graphql-client', {