@wpnuxt/core 2.0.0-beta.2 → 2.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +2 -2
- package/dist/module.mjs +110 -258
- package/dist/runtime/app/graphqlMiddleware.clientOptions.js +1 -1
- package/dist/runtime/composables/usePrevNextPost.js +2 -2
- package/dist/runtime/plugins/sanitizeHtml.js +20 -14
- package/dist/runtime/queries/Pages.gql +2 -2
- package/dist/runtime/queries/fragments/MediaItem.fragment.gql +15 -1
- package/dist/runtime/server/graphqlMiddleware.serverOptions.js +14 -4
- package/package.json +4 -5
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { consola } from 'consola';
|
|
2
1
|
import { defu } from 'defu';
|
|
3
|
-
import { promises, cpSync, existsSync, readdirSync, statSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { promises, cpSync, existsSync, readdirSync, statSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
3
|
import { writeFile, rename, readFile, mkdir } from 'node:fs/promises';
|
|
5
4
|
import { join, relative, dirname } from 'node:path';
|
|
6
|
-
import { useLogger, createResolver, resolveFiles, defineNuxtModule, addPlugin,
|
|
5
|
+
import { useLogger, createResolver, resolveFiles, defineNuxtModule, addPlugin, addImports, addComponentsDir, addTemplate, addTypeTemplate, hasNuxtModule, installModule } from '@nuxt/kit';
|
|
7
6
|
import { upperFirst } from 'scule';
|
|
8
|
-
import {
|
|
9
|
-
import { parse, GraphQLError } from 'graphql';
|
|
7
|
+
import { parse, GraphQLError, visit, print } from 'graphql';
|
|
10
8
|
import { execSync } from 'node:child_process';
|
|
9
|
+
import { consola } from 'consola';
|
|
11
10
|
|
|
12
|
-
const version = "2.0.0-beta.
|
|
11
|
+
const version = "2.0.0-beta.4";
|
|
13
12
|
|
|
14
13
|
function createModuleError(module, message) {
|
|
15
14
|
return new Error(formatErrorMessage(module, message));
|
|
@@ -44,13 +43,13 @@ async function atomicWriteFile(path, content) {
|
|
|
44
43
|
function randHashGenerator(length = 12) {
|
|
45
44
|
return Math.random().toString(36).substring(2, 2 + length).toUpperCase().padEnd(length, "0");
|
|
46
45
|
}
|
|
47
|
-
|
|
46
|
+
let loggerInstance;
|
|
48
47
|
const initLogger = (debug) => {
|
|
49
|
-
|
|
50
|
-
return
|
|
48
|
+
loggerInstance = useLogger("wpnuxt", { level: debug ? 4 : 3 });
|
|
49
|
+
return loggerInstance;
|
|
51
50
|
};
|
|
52
51
|
function getLogger() {
|
|
53
|
-
return
|
|
52
|
+
return loggerInstance;
|
|
54
53
|
}
|
|
55
54
|
async function mergeQueries(nuxt, wpNuxtConfig, resolver) {
|
|
56
55
|
const logger = getLogger();
|
|
@@ -70,7 +69,7 @@ async function mergeQueries(nuxt, wpNuxtConfig, resolver) {
|
|
|
70
69
|
}
|
|
71
70
|
if (existsSync(userQueryPath)) {
|
|
72
71
|
logger.debug("Extending queries:", userQueryPath);
|
|
73
|
-
|
|
72
|
+
copyGraphqlFiles(userQueryPath, queryOutputPath);
|
|
74
73
|
}
|
|
75
74
|
logger.debug("Merged queries folder:", queryOutputPath);
|
|
76
75
|
return queryOutputPath;
|
|
@@ -98,6 +97,20 @@ function findConflicts(userQueryPath, outputPath) {
|
|
|
98
97
|
}
|
|
99
98
|
return conflicts;
|
|
100
99
|
}
|
|
100
|
+
function copyGraphqlFiles(src, dest) {
|
|
101
|
+
const entries = readdirSync(src);
|
|
102
|
+
for (const entry of entries) {
|
|
103
|
+
const srcPath = join(src, entry);
|
|
104
|
+
const destPath = join(dest, entry);
|
|
105
|
+
const stat = statSync(srcPath);
|
|
106
|
+
if (stat.isDirectory()) {
|
|
107
|
+
mkdirSync(destPath, { recursive: true });
|
|
108
|
+
copyGraphqlFiles(srcPath, destPath);
|
|
109
|
+
} else if (entry.endsWith(".gql") || entry.endsWith(".graphql")) {
|
|
110
|
+
cpSync(srcPath, destPath);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
101
114
|
|
|
102
115
|
const _parseDoc = async (doc) => {
|
|
103
116
|
if (!doc || typeof doc !== "string" || doc.trim().length === 0) {
|
|
@@ -110,8 +123,12 @@ const _parseDoc = async (doc) => {
|
|
|
110
123
|
if (!operationDefinition.name?.value) {
|
|
111
124
|
throw createModuleError("core", "GraphQL operation is missing a name. All queries and mutations must have a name.");
|
|
112
125
|
}
|
|
126
|
+
const name = operationDefinition.name.value.trim();
|
|
127
|
+
if (!/^[a-z_$][\w$]*$/i.test(name)) {
|
|
128
|
+
throw createModuleError("core", `Invalid GraphQL operation name "${name}". Operation names must be valid JavaScript identifiers (start with a letter, underscore, or $ and contain only letters, numbers, underscores, or $).`);
|
|
129
|
+
}
|
|
113
130
|
const query = {
|
|
114
|
-
name
|
|
131
|
+
name,
|
|
115
132
|
nodes: [],
|
|
116
133
|
fragments: [],
|
|
117
134
|
params: {},
|
|
@@ -281,7 +298,17 @@ async function prepareContext(ctx) {
|
|
|
281
298
|
" watch?: unknown[]",
|
|
282
299
|
" /** Transform function to alter the result */",
|
|
283
300
|
" transform?: (input: unknown) => unknown",
|
|
284
|
-
" /**
|
|
301
|
+
" /** Enable client-side GraphQL caching. Default: true. Set to false for real-time data. */",
|
|
302
|
+
" clientCache?: boolean",
|
|
303
|
+
" /** Custom function to control when cached data should be used. */",
|
|
304
|
+
" getCachedData?: (key: string, nuxtApp: unknown, ctx: unknown) => unknown",
|
|
305
|
+
" /** Number of automatic retries on failure. Set to 0 or false to disable. Default: 0 (disabled) */",
|
|
306
|
+
" retry?: number | false",
|
|
307
|
+
" /** Base delay in milliseconds between retries (uses exponential backoff). Default: 1000 */",
|
|
308
|
+
" retryDelay?: number",
|
|
309
|
+
" /** Request timeout in milliseconds. Default: 0 (disabled). Set to e.g. 30000 for 30 seconds. */",
|
|
310
|
+
" timeout?: number",
|
|
311
|
+
" /** Additional options to pass to useAsyncData */",
|
|
285
312
|
" [key: string]: unknown",
|
|
286
313
|
"}",
|
|
287
314
|
"",
|
|
@@ -473,29 +500,31 @@ Check your wpNuxt.wordpressUrl configuration in nuxt.config.ts`
|
|
|
473
500
|
);
|
|
474
501
|
}
|
|
475
502
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
);
|
|
489
|
-
schema = schema.replace(
|
|
490
|
-
new RegExp(`implements\\s+${iface}\\s+&\\s+`, "g"),
|
|
491
|
-
"implements "
|
|
492
|
-
);
|
|
493
|
-
schema = schema.replace(
|
|
494
|
-
new RegExp(`\\s*&\\s*${iface}(?=\\s*[&{])`, "g"),
|
|
495
|
-
""
|
|
503
|
+
const PROBLEMATIC_INTERFACES = /* @__PURE__ */ new Set([
|
|
504
|
+
"Connection",
|
|
505
|
+
"Edge",
|
|
506
|
+
"OneToOneConnection",
|
|
507
|
+
"NodeWithEditorBlocks"
|
|
508
|
+
]);
|
|
509
|
+
function patchSchemaText(schemaText) {
|
|
510
|
+
const ast = parse(schemaText);
|
|
511
|
+
function filterInterfaces(node) {
|
|
512
|
+
if (!node.interfaces?.length) return void 0;
|
|
513
|
+
const filtered = node.interfaces.filter(
|
|
514
|
+
(iface) => !PROBLEMATIC_INTERFACES.has(iface.name.value)
|
|
496
515
|
);
|
|
516
|
+
if (filtered.length === node.interfaces.length) return void 0;
|
|
517
|
+
return { ...node, interfaces: filtered };
|
|
497
518
|
}
|
|
498
|
-
|
|
519
|
+
const patched = visit(ast, {
|
|
520
|
+
ObjectTypeDefinition: filterInterfaces,
|
|
521
|
+
InterfaceTypeDefinition: filterInterfaces
|
|
522
|
+
});
|
|
523
|
+
return print(patched);
|
|
524
|
+
}
|
|
525
|
+
function patchWPGraphQLSchema(schemaPath) {
|
|
526
|
+
const schema = readFileSync(schemaPath, "utf-8");
|
|
527
|
+
writeFileSync(schemaPath, patchSchemaText(schema));
|
|
499
528
|
}
|
|
500
529
|
|
|
501
530
|
async function runInstall(nuxt) {
|
|
@@ -548,71 +577,31 @@ WPNUXT_WORDPRESS_URL=https://your-wordpress-site.com
|
|
|
548
577
|
# Optional: Enable debug mode for verbose logging
|
|
549
578
|
# WPNUXT_DEBUG=true
|
|
550
579
|
`;
|
|
551
|
-
function isInteractiveEnvironment() {
|
|
552
|
-
return !(process.env.CI === "true" || process.env.CI === "1" || process.env.VITEST === "true" || process.env.TEST === "true" || process.env.NODE_ENV === "test");
|
|
553
|
-
}
|
|
554
580
|
async function checkExistingEnvConfig(nuxt, envPath) {
|
|
555
581
|
const nuxtConfig = nuxt.options;
|
|
556
582
|
if (nuxtConfig.wpNuxt?.wordpressUrl) {
|
|
557
|
-
return { hasUrl: true, source: "nuxt.config.ts"
|
|
583
|
+
return { hasUrl: true, source: "nuxt.config.ts" };
|
|
558
584
|
}
|
|
559
585
|
if (process.env.WPNUXT_WORDPRESS_URL) {
|
|
560
|
-
return { hasUrl: true, source: "WPNUXT_WORDPRESS_URL env var"
|
|
586
|
+
return { hasUrl: true, source: "WPNUXT_WORDPRESS_URL env var" };
|
|
561
587
|
}
|
|
562
588
|
if (existsSync(envPath)) {
|
|
563
589
|
const envContent = await readFile(envPath, "utf-8");
|
|
564
590
|
if (/^WPNUXT_WORDPRESS_URL\s*=\s*.+/m.test(envContent)) {
|
|
565
|
-
return { hasUrl: true, source: ".env file"
|
|
591
|
+
return { hasUrl: true, source: ".env file" };
|
|
566
592
|
}
|
|
567
|
-
return { hasUrl: false
|
|
593
|
+
return { hasUrl: false };
|
|
568
594
|
}
|
|
569
|
-
return { hasUrl: false
|
|
595
|
+
return { hasUrl: false };
|
|
570
596
|
}
|
|
571
597
|
async function setupEnvFiles(nuxt, logger) {
|
|
572
598
|
const envPath = join(nuxt.options.rootDir, ".env");
|
|
573
599
|
const envExamplePath = join(nuxt.options.rootDir, ".env.example");
|
|
574
600
|
try {
|
|
575
601
|
const existingConfig = await checkExistingEnvConfig(nuxt, envPath);
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
if (existingConfig.hasUrl) {
|
|
602
|
+
const urlConfigured = existingConfig.hasUrl;
|
|
603
|
+
if (urlConfigured) {
|
|
579
604
|
logger.debug(`WordPress URL already configured in ${existingConfig.source}`);
|
|
580
|
-
urlConfigured = true;
|
|
581
|
-
} else if (isInteractiveEnvironment()) {
|
|
582
|
-
consola.box({
|
|
583
|
-
title: "WPNuxt Setup",
|
|
584
|
-
message: "Configure your WordPress connection"
|
|
585
|
-
});
|
|
586
|
-
const wordpressUrl = await consola.prompt(
|
|
587
|
-
"What is your WordPress site URL?",
|
|
588
|
-
{
|
|
589
|
-
type: "text",
|
|
590
|
-
placeholder: "https://your-wordpress-site.com",
|
|
591
|
-
initial: ""
|
|
592
|
-
}
|
|
593
|
-
);
|
|
594
|
-
if (wordpressUrl && typeof wordpressUrl === "string" && wordpressUrl.trim()) {
|
|
595
|
-
const validation = validateWordPressUrl(wordpressUrl);
|
|
596
|
-
if (!validation.valid) {
|
|
597
|
-
logger.warn(`Invalid URL: ${validation.error}`);
|
|
598
|
-
logger.info("Skipped WordPress URL configuration. Add WPNUXT_WORDPRESS_URL to your .env file later.");
|
|
599
|
-
} else {
|
|
600
|
-
const envLine = `WPNUXT_WORDPRESS_URL=${validation.normalizedUrl}
|
|
601
|
-
`;
|
|
602
|
-
if (envContent) {
|
|
603
|
-
envContent = envContent.trimEnd() + "\n\n" + envLine;
|
|
604
|
-
} else {
|
|
605
|
-
envContent = envLine;
|
|
606
|
-
}
|
|
607
|
-
await atomicWriteFile(envPath, envContent);
|
|
608
|
-
logger.success(`WordPress URL saved to .env: ${validation.normalizedUrl}`);
|
|
609
|
-
urlConfigured = true;
|
|
610
|
-
}
|
|
611
|
-
} else {
|
|
612
|
-
logger.info("Skipped WordPress URL configuration. Add WPNUXT_WORDPRESS_URL to your .env file later.");
|
|
613
|
-
}
|
|
614
|
-
} else {
|
|
615
|
-
logger.debug("Non-interactive environment detected, skipping WordPress URL prompt");
|
|
616
605
|
}
|
|
617
606
|
let exampleContent = "";
|
|
618
607
|
let exampleUpdated = false;
|
|
@@ -714,65 +703,23 @@ async function setupGitignore(nuxt, logger) {
|
|
|
714
703
|
return { name: "Gitignore", success: false, message };
|
|
715
704
|
}
|
|
716
705
|
}
|
|
717
|
-
const QUERIES_README = `# Custom GraphQL Queries
|
|
718
|
-
|
|
719
|
-
Place your custom \`.gql\` or \`.graphql\` files here to extend or override the default WPNuxt queries.
|
|
720
|
-
|
|
721
|
-
## How it works
|
|
722
|
-
|
|
723
|
-
1. Files here are merged with WPNuxt's default queries during build
|
|
724
|
-
2. If a file has the same name as a default query, yours will override it
|
|
725
|
-
3. New files will generate new composables automatically
|
|
726
|
-
|
|
727
|
-
## Example
|
|
728
|
-
|
|
729
|
-
Create a file \`CustomPosts.gql\`:
|
|
730
|
-
|
|
731
|
-
\`\`\`graphql
|
|
732
|
-
query CustomPosts($first: Int = 10) {
|
|
733
|
-
posts(first: $first) {
|
|
734
|
-
nodes {
|
|
735
|
-
id
|
|
736
|
-
title
|
|
737
|
-
date
|
|
738
|
-
# Add your custom fields here
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
\`\`\`
|
|
743
|
-
|
|
744
|
-
This generates \`useCustomPosts()\` and \`useAsyncCustomPosts()\` composables.
|
|
745
|
-
|
|
746
|
-
## Available Fragments
|
|
747
|
-
|
|
748
|
-
You can use these fragments from WPNuxt's defaults:
|
|
749
|
-
- \`...Post\` - Standard post fields
|
|
750
|
-
- \`...Page\` - Standard page fields
|
|
751
|
-
- \`...ContentNode\` - Common content fields
|
|
752
|
-
- \`...FeaturedImage\` - Featured image with sizes
|
|
753
|
-
|
|
754
|
-
## Documentation
|
|
755
|
-
|
|
756
|
-
See https://wpnuxt.com/guide/custom-queries for more details.
|
|
757
|
-
`;
|
|
758
706
|
async function setupQueriesFolder(nuxt, logger) {
|
|
759
|
-
const queriesPath = join(nuxt.options.rootDir, "extend", "queries");
|
|
760
|
-
const
|
|
707
|
+
const queriesPath = join(nuxt.options.srcDir || nuxt.options.rootDir, "extend", "queries");
|
|
708
|
+
const displayPath = relative(nuxt.options.rootDir, queriesPath) || "extend/queries";
|
|
761
709
|
try {
|
|
762
|
-
if (existsSync(
|
|
763
|
-
logger.debug(
|
|
710
|
+
if (existsSync(queriesPath)) {
|
|
711
|
+
logger.debug(`${displayPath}/ folder already exists`);
|
|
764
712
|
return { name: "Queries folder", success: true, skipped: true };
|
|
765
713
|
}
|
|
766
714
|
await mkdir(queriesPath, { recursive: true });
|
|
767
|
-
await atomicWriteFile(readmePath, QUERIES_README);
|
|
768
715
|
return {
|
|
769
716
|
name: "Queries folder",
|
|
770
717
|
success: true,
|
|
771
|
-
message:
|
|
718
|
+
message: `Created ${displayPath}/ for custom GraphQL queries`
|
|
772
719
|
};
|
|
773
720
|
} catch (error) {
|
|
774
721
|
const message = error instanceof Error ? error.message : String(error);
|
|
775
|
-
logger.warn(`Failed to setup
|
|
722
|
+
logger.warn(`Failed to setup ${displayPath}/ folder: ${message}`);
|
|
776
723
|
return { name: "Queries folder", success: false, message };
|
|
777
724
|
}
|
|
778
725
|
}
|
|
@@ -783,7 +730,7 @@ const module$1 = defineNuxtModule({
|
|
|
783
730
|
version,
|
|
784
731
|
configKey: "wpNuxt",
|
|
785
732
|
compatibility: {
|
|
786
|
-
nuxt: ">=3.
|
|
733
|
+
nuxt: ">=3.17.0"
|
|
787
734
|
}
|
|
788
735
|
},
|
|
789
736
|
defaults: {
|
|
@@ -882,29 +829,26 @@ const module$1 = defineNuxtModule({
|
|
|
882
829
|
};
|
|
883
830
|
logger.debug(`Configured WordPress uploads proxy: /wp-content/uploads/** \u2192 ${wpNuxtConfig.wordpressUrl}/wp-content/uploads/**`);
|
|
884
831
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
domains.push(wpHost);
|
|
894
|
-
}
|
|
895
|
-
imageConfig.domains = domains;
|
|
896
|
-
const alias = imageConfig.alias || {};
|
|
897
|
-
alias["/wp-content"] = `${wpNuxtConfig.wordpressUrl}/wp-content`;
|
|
898
|
-
imageConfig.alias = alias;
|
|
899
|
-
nuxt.options.image = imageConfig;
|
|
900
|
-
logger.debug(`Configured IPX for WordPress: alias /wp-content \u2192 ${wpNuxtConfig.wordpressUrl}/wp-content, domain '${wpHost}' added`);
|
|
832
|
+
{
|
|
833
|
+
const imageConfig = nuxt.options.image || {};
|
|
834
|
+
const provider = process.env.NUXT_IMAGE_PROVIDER || imageConfig.provider || "ipx";
|
|
835
|
+
if (provider === "ipx") {
|
|
836
|
+
const wpHost = new URL(wpNuxtConfig.wordpressUrl).host;
|
|
837
|
+
const domains = imageConfig.domains || [];
|
|
838
|
+
if (!domains.includes(wpHost)) {
|
|
839
|
+
domains.push(wpHost);
|
|
901
840
|
}
|
|
841
|
+
imageConfig.domains = domains;
|
|
842
|
+
const alias = imageConfig.alias || {};
|
|
843
|
+
alias["/wp-content"] = `${wpNuxtConfig.wordpressUrl}/wp-content`;
|
|
844
|
+
imageConfig.alias = alias;
|
|
845
|
+
nuxt.options.image = imageConfig;
|
|
846
|
+
logger.debug(`Configured IPX for WordPress: alias /wp-content \u2192 ${wpNuxtConfig.wordpressUrl}/wp-content, domain '${wpHost}' added`);
|
|
902
847
|
}
|
|
903
|
-
}
|
|
848
|
+
}
|
|
904
849
|
configureVercelSettings(nuxt, logger);
|
|
905
850
|
addImports([
|
|
906
851
|
{ name: "useWPContent", as: "useWPContent", from: resolver.resolve("./runtime/composables/useWPContent") },
|
|
907
|
-
{ name: "useAsyncWPContent", as: "useAsyncWPContent", from: resolver.resolve("./runtime/composables/useWPContent") },
|
|
908
852
|
{ name: "getRelativeImagePath", as: "getRelativeImagePath", from: resolver.resolve("./runtime/util/images") },
|
|
909
853
|
{ name: "isInternalLink", as: "isInternalLink", from: resolver.resolve("./runtime/util/links") },
|
|
910
854
|
{ name: "toRelativePath", as: "toRelativePath", from: resolver.resolve("./runtime/util/links") },
|
|
@@ -971,38 +915,13 @@ async function loadConfig(options, nuxt) {
|
|
|
971
915
|
if (nuxt.options._prepare) {
|
|
972
916
|
return null;
|
|
973
917
|
}
|
|
974
|
-
|
|
975
|
-
const wordpressUrl = await consola.prompt(
|
|
976
|
-
"Enter your WordPress site URL (must have WPGraphQL installed):",
|
|
977
|
-
{ type: "text", placeholder: "https://your-wordpress-site.com" }
|
|
978
|
-
);
|
|
979
|
-
if (wordpressUrl && typeof wordpressUrl === "string" && wordpressUrl.trim()) {
|
|
980
|
-
const validation = validateWordPressUrl(wordpressUrl);
|
|
981
|
-
if (validation.valid && validation.normalizedUrl) {
|
|
982
|
-
config.wordpressUrl = validation.normalizedUrl;
|
|
983
|
-
const envPath = join(nuxt.options.rootDir, ".env");
|
|
984
|
-
const envLine = `WPNUXT_WORDPRESS_URL=${validation.normalizedUrl}
|
|
985
|
-
`;
|
|
986
|
-
if (existsSync(envPath)) {
|
|
987
|
-
const existing = await readFile(envPath, "utf-8");
|
|
988
|
-
await atomicWriteFile(envPath, existing.trimEnd() + "\n" + envLine);
|
|
989
|
-
} else {
|
|
990
|
-
await atomicWriteFile(envPath, envLine);
|
|
991
|
-
}
|
|
992
|
-
consola.success(`WordPress URL saved to .env: ${validation.normalizedUrl}`);
|
|
993
|
-
} else {
|
|
994
|
-
throw createModuleError("core", `Invalid WordPress URL: ${validation.error}`);
|
|
995
|
-
}
|
|
996
|
-
} else {
|
|
997
|
-
throw createModuleError("core", "WordPress URL is required. Set it in nuxt.config.ts or via WPNUXT_WORDPRESS_URL environment variable.");
|
|
998
|
-
}
|
|
999
|
-
} else {
|
|
1000
|
-
throw createModuleError("core", "WordPress URL is required. Set it in nuxt.config.ts or via WPNUXT_WORDPRESS_URL environment variable.");
|
|
1001
|
-
}
|
|
918
|
+
throw createModuleError("core", "WordPress URL is required. Set it in nuxt.config.ts or via WPNUXT_WORDPRESS_URL environment variable.");
|
|
1002
919
|
}
|
|
1003
|
-
|
|
1004
|
-
|
|
920
|
+
const validation = validateWordPressUrl(config.wordpressUrl);
|
|
921
|
+
if (!validation.valid) {
|
|
922
|
+
throw createModuleError("core", `Invalid WordPress URL: ${validation.error}`);
|
|
1005
923
|
}
|
|
924
|
+
config.wordpressUrl = validation.normalizedUrl;
|
|
1006
925
|
nuxt.options.runtimeConfig.public.wordpressUrl = config.wordpressUrl;
|
|
1007
926
|
nuxt.options.runtimeConfig.public.wpNuxt = {
|
|
1008
927
|
wordpressUrl: config.wordpressUrl,
|
|
@@ -1018,53 +937,7 @@ async function loadConfig(options, nuxt) {
|
|
|
1018
937
|
};
|
|
1019
938
|
return config;
|
|
1020
939
|
}
|
|
1021
|
-
|
|
1022
|
-
import { getHeader, getCookie } from 'h3'
|
|
1023
|
-
import { useRuntimeConfig } from '#imports'
|
|
1024
|
-
|
|
1025
|
-
/**
|
|
1026
|
-
* WPNuxt default server options for nuxt-graphql-middleware.
|
|
1027
|
-
*
|
|
1028
|
-
* This enables:
|
|
1029
|
-
* - Cookie forwarding for WordPress preview mode
|
|
1030
|
-
* - Authorization header forwarding for authenticated requests
|
|
1031
|
-
* - Auth token from cookie for @wpnuxt/auth
|
|
1032
|
-
* - Consistent error logging
|
|
1033
|
-
*
|
|
1034
|
-
* Users can customize by creating their own server/graphqlMiddleware.serverOptions.ts
|
|
1035
|
-
*/
|
|
1036
|
-
export default defineGraphqlServerOptions({
|
|
1037
|
-
async serverFetchOptions(event, _operation, _operationName, _context) {
|
|
1038
|
-
// Get auth token from Authorization header or from cookie
|
|
1039
|
-
let authorization = getHeader(event, 'authorization') || ''
|
|
1040
|
-
|
|
1041
|
-
// If no Authorization header, check for auth token in cookie (@wpnuxt/auth)
|
|
1042
|
-
if (!authorization) {
|
|
1043
|
-
const config = useRuntimeConfig().public.wpNuxtAuth as { cookieName?: string } | undefined
|
|
1044
|
-
const cookieName = config?.cookieName || 'wpnuxt-auth-token'
|
|
1045
|
-
const authToken = getCookie(event, cookieName)
|
|
1046
|
-
if (authToken) {
|
|
1047
|
-
authorization = \`Bearer \${authToken}\`
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
return {
|
|
1052
|
-
headers: {
|
|
1053
|
-
// Forward WordPress auth cookies for previews
|
|
1054
|
-
Cookie: getHeader(event, 'cookie') || '',
|
|
1055
|
-
// Forward authorization header or token from cookie
|
|
1056
|
-
Authorization: authorization
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
},
|
|
1060
|
-
|
|
1061
|
-
async onServerError(event, error, _operation, operationName) {
|
|
1062
|
-
const url = event.node.req.url || 'unknown'
|
|
1063
|
-
console.error(\`[WPNuxt] GraphQL error in \${operationName} (\${url}):\`, error.message)
|
|
1064
|
-
}
|
|
1065
|
-
})
|
|
1066
|
-
`;
|
|
1067
|
-
async function setupServerOptions(nuxt, _resolver, logger) {
|
|
940
|
+
async function setupServerOptions(nuxt, resolver, logger) {
|
|
1068
941
|
const serverDir = nuxt.options.serverDir;
|
|
1069
942
|
const targetPath = join(serverDir, "graphqlMiddleware.serverOptions.ts");
|
|
1070
943
|
if (existsSync(targetPath)) {
|
|
@@ -1074,39 +947,14 @@ async function setupServerOptions(nuxt, _resolver, logger) {
|
|
|
1074
947
|
if (!existsSync(serverDir)) {
|
|
1075
948
|
await mkdir(serverDir, { recursive: true });
|
|
1076
949
|
}
|
|
1077
|
-
|
|
950
|
+
const template = readFileSync(
|
|
951
|
+
resolver.resolve("./runtime/server/graphqlMiddleware.serverOptions.ts"),
|
|
952
|
+
"utf-8"
|
|
953
|
+
);
|
|
954
|
+
await writeFile(targetPath, template);
|
|
1078
955
|
logger.debug("Created graphqlMiddleware.serverOptions.ts with WPNuxt defaults (cookie/auth forwarding)");
|
|
1079
956
|
}
|
|
1080
|
-
|
|
1081
|
-
import { useRoute } from '#imports'
|
|
1082
|
-
|
|
1083
|
-
/**
|
|
1084
|
-
* WPNuxt default client options for nuxt-graphql-middleware.
|
|
1085
|
-
*
|
|
1086
|
-
* This enables passing client context to the server for:
|
|
1087
|
-
* - Preview mode (passes preview flag and token from URL query params)
|
|
1088
|
-
*
|
|
1089
|
-
* The context is available in serverFetchOptions via context.client
|
|
1090
|
-
* All values must be strings (nuxt-graphql-middleware requirement)
|
|
1091
|
-
*
|
|
1092
|
-
* Users can customize by creating their own app/graphqlMiddleware.clientOptions.ts
|
|
1093
|
-
*/
|
|
1094
|
-
export default defineGraphqlClientOptions<{
|
|
1095
|
-
preview?: string
|
|
1096
|
-
previewToken?: string
|
|
1097
|
-
}>({
|
|
1098
|
-
buildClientContext() {
|
|
1099
|
-
const route = useRoute()
|
|
1100
|
-
|
|
1101
|
-
return {
|
|
1102
|
-
// Context values must be strings - use 'true'/'false' instead of boolean
|
|
1103
|
-
preview: route.query.preview === 'true' ? 'true' : undefined,
|
|
1104
|
-
previewToken: route.query.token as string | undefined
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
})
|
|
1108
|
-
`;
|
|
1109
|
-
async function setupClientOptions(nuxt, _resolver, logger) {
|
|
957
|
+
async function setupClientOptions(nuxt, resolver, logger) {
|
|
1110
958
|
const appDir = nuxt.options.dir.app;
|
|
1111
959
|
const targetPath = join(appDir, "graphqlMiddleware.clientOptions.ts");
|
|
1112
960
|
if (existsSync(targetPath)) {
|
|
@@ -1116,11 +964,15 @@ async function setupClientOptions(nuxt, _resolver, logger) {
|
|
|
1116
964
|
if (!existsSync(appDir)) {
|
|
1117
965
|
await mkdir(appDir, { recursive: true });
|
|
1118
966
|
}
|
|
1119
|
-
|
|
967
|
+
const template = readFileSync(
|
|
968
|
+
resolver.resolve("./runtime/app/graphqlMiddleware.clientOptions.ts"),
|
|
969
|
+
"utf-8"
|
|
970
|
+
);
|
|
971
|
+
await writeFile(targetPath, template);
|
|
1120
972
|
logger.debug("Created graphqlMiddleware.clientOptions.ts with WPNuxt defaults (preview mode support)");
|
|
1121
973
|
}
|
|
1122
974
|
function configureTrailingSlash(nuxt, logger) {
|
|
1123
|
-
const handlerPath = join(nuxt.options.buildDir, "wpnuxt", "trailing-slash-handler.ts");
|
|
975
|
+
const handlerPath = join(nuxt.options.buildDir, "wpnuxt", "trailing-slash-handler.ts").replace(/\\/g, "/");
|
|
1124
976
|
const handlerCode = `import { defineEventHandler, sendRedirect, getRequestURL } from 'h3'
|
|
1125
977
|
|
|
1126
978
|
export default defineEventHandler((event) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useWPContent } from "./useWPContent.js";
|
|
2
|
-
export async function usePrevNextPost(currentSlug) {
|
|
3
|
-
const { data, execute } = useWPContent("Posts", ["posts", "nodes"], false, { limit
|
|
2
|
+
export async function usePrevNextPost(currentSlug, limit = 1e3) {
|
|
3
|
+
const { data, execute } = useWPContent("Posts", ["posts", "nodes"], false, { limit });
|
|
4
4
|
await execute();
|
|
5
5
|
const allPosts = data.value || [];
|
|
6
6
|
if (!allPosts.length) {
|
|
@@ -1,29 +1,35 @@
|
|
|
1
1
|
import { defineNuxtPlugin } from "#imports";
|
|
2
2
|
export default defineNuxtPlugin((nuxtApp) => {
|
|
3
|
-
let sanitize;
|
|
4
3
|
if (import.meta.server) {
|
|
5
|
-
sanitize
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
sanitize = (html) => {
|
|
9
|
-
if (purify) {
|
|
10
|
-
return purify.sanitize(html);
|
|
4
|
+
nuxtApp.vueApp.directive("sanitize-html", {
|
|
5
|
+
getSSRProps(binding) {
|
|
6
|
+
return { innerHTML: binding.value };
|
|
11
7
|
}
|
|
12
|
-
return html;
|
|
13
|
-
};
|
|
14
|
-
import("dompurify").then((DOMPurify) => {
|
|
15
|
-
purify = DOMPurify.default(window);
|
|
16
8
|
});
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
let purify = null;
|
|
12
|
+
const purifyReady = import("dompurify").then((DOMPurify) => {
|
|
13
|
+
purify = DOMPurify.default(window);
|
|
14
|
+
});
|
|
15
|
+
function setSanitizedHtml(el, html) {
|
|
16
|
+
if (purify) {
|
|
17
|
+
el.innerHTML = purify.sanitize(html);
|
|
18
|
+
} else {
|
|
19
|
+
purifyReady.then(() => {
|
|
20
|
+
el.innerHTML = purify.sanitize(html);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
17
23
|
}
|
|
18
24
|
nuxtApp.vueApp.directive("sanitize-html", {
|
|
19
25
|
created(el, binding) {
|
|
20
|
-
el
|
|
26
|
+
setSanitizedHtml(el, binding.value);
|
|
21
27
|
},
|
|
22
28
|
updated(el, binding) {
|
|
23
|
-
el
|
|
29
|
+
setSanitizedHtml(el, binding.value);
|
|
24
30
|
},
|
|
25
31
|
getSSRProps(binding) {
|
|
26
|
-
return { innerHTML:
|
|
32
|
+
return { innerHTML: binding.value };
|
|
27
33
|
}
|
|
28
34
|
});
|
|
29
35
|
});
|
|
@@ -10,8 +10,8 @@ query PageByUri($uri: ID!) {
|
|
|
10
10
|
...Page
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
query PageById($id: ID
|
|
14
|
-
page(id: $id, idType: DATABASE_ID, asPreview:
|
|
13
|
+
query PageById($id: ID!, $asPreview: Boolean = false) {
|
|
14
|
+
page(id: $id, idType: DATABASE_ID, asPreview: $asPreview) {
|
|
15
15
|
...Page
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -29,7 +29,21 @@ fragment MediaItem on MediaItem {
|
|
|
29
29
|
isTermNode
|
|
30
30
|
# lastEditedBy
|
|
31
31
|
link
|
|
32
|
-
|
|
32
|
+
mediaDetails {
|
|
33
|
+
file
|
|
34
|
+
filePath
|
|
35
|
+
height
|
|
36
|
+
width
|
|
37
|
+
sizes {
|
|
38
|
+
file
|
|
39
|
+
fileSize
|
|
40
|
+
height
|
|
41
|
+
mimeType
|
|
42
|
+
name
|
|
43
|
+
sourceUrl
|
|
44
|
+
width
|
|
45
|
+
}
|
|
46
|
+
}
|
|
33
47
|
mediaItemId
|
|
34
48
|
mediaItemUrl
|
|
35
49
|
mediaType
|
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
import { defineGraphqlServerOptions } from "
|
|
2
|
-
import { getHeader } from "h3";
|
|
1
|
+
import { defineGraphqlServerOptions } from "@wpnuxt/core/server-options";
|
|
2
|
+
import { getHeader, getCookie } from "h3";
|
|
3
|
+
import { useRuntimeConfig } from "#imports";
|
|
3
4
|
export default defineGraphqlServerOptions({
|
|
4
5
|
async serverFetchOptions(event, _operation, _operationName, _context) {
|
|
6
|
+
let authorization = getHeader(event, "authorization") || "";
|
|
7
|
+
if (!authorization) {
|
|
8
|
+
const config = useRuntimeConfig().public.wpNuxtAuth;
|
|
9
|
+
const cookieName = config?.cookieName || "wpnuxt-auth-token";
|
|
10
|
+
const authToken = getCookie(event, cookieName);
|
|
11
|
+
if (authToken) {
|
|
12
|
+
authorization = `Bearer ${authToken}`;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
5
15
|
return {
|
|
6
16
|
headers: {
|
|
7
17
|
// Forward WordPress auth cookies for previews
|
|
8
18
|
Cookie: getHeader(event, "cookie") || "",
|
|
9
|
-
// Forward authorization header
|
|
10
|
-
Authorization:
|
|
19
|
+
// Forward authorization header or token from cookie
|
|
20
|
+
Authorization: authorization
|
|
11
21
|
}
|
|
12
22
|
};
|
|
13
23
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wpnuxt/core",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.4",
|
|
4
4
|
"description": "Nuxt module for WordPress integration via GraphQL (WPGraphQL)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nuxt",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"scule": "^1.3.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@nuxt/devtools": "^3.2.
|
|
58
|
+
"@nuxt/devtools": "^3.2.1",
|
|
59
59
|
"@nuxt/module-builder": "^1.0.2",
|
|
60
60
|
"@nuxt/schema": "4.3.1",
|
|
61
61
|
"@nuxt/test-utils": "^4.0.0",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"vue-tsc": "^3.2.3"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
|
-
"nuxt": "
|
|
68
|
+
"nuxt": ">=3.17.0"
|
|
69
69
|
},
|
|
70
70
|
"scripts": {
|
|
71
71
|
"build": "nuxt-module-build build",
|
|
@@ -73,7 +73,6 @@
|
|
|
73
73
|
"lint": "eslint src",
|
|
74
74
|
"test": "vitest run",
|
|
75
75
|
"test:watch": "vitest watch",
|
|
76
|
-
"typecheck": "vue-tsc --noEmit"
|
|
77
|
-
"clean": "rm -rf dist .nuxt node_modules"
|
|
76
|
+
"typecheck": "vue-tsc --noEmit"
|
|
78
77
|
}
|
|
79
78
|
}
|