├── .editorconfig ├── .github ├── renovate.json └── workflows │ └── ci.yml ├── .gitignore ├── .markdownlint.yml ├── .markdownlintignore ├── .npmrc ├── .nuxtrc ├── .release-it-edge.json ├── .release-it.json ├── .vscode └── settings.json ├── .wp-env.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs ├── 0.index.md ├── 1.getting-started │ ├── 1.index.md │ ├── 2.configuration.md │ ├── 3.composables.md │ ├── 4.components.md │ ├── 5.authentication.md │ ├── 6.images.md │ ├── 7.typescript.md │ └── _dir.yml ├── 2.composables │ ├── 0.index.md │ ├── 1.posts.md │ ├── 2.pages.md │ ├── 3.nodes.md │ ├── 4.menu.md │ ├── 5.settings.md │ ├── 6.viewer.md │ ├── 7.custom-queries.md │ ├── 8.staging.md │ ├── 9.previous-next-post.md │ └── _dir.yml ├── 3.other-composables │ └── _dir.yml ├── 4.advanced │ ├── 1.index.md │ ├── 2.extending-posts.md │ ├── 3.customcomponents.md │ ├── 4.staging.md │ └── _dir.yml ├── 5.localdev │ ├── 1.index.md │ ├── 2.docker-compose.md │ ├── 3.localwp.md │ └── _dir.yml ├── 6.under-the-hood │ ├── 1.index.md │ ├── 2.code-generation.md │ └── _dir.yml ├── 7.troubleshoot │ ├── 1.index.md │ └── _dir.yml └── index.yml ├── eslint.config.mjs ├── package.json ├── playground ├── .env-example ├── .npmrc ├── .nuxtrc ├── app │ ├── app.config.ts │ ├── app.vue │ ├── assets │ │ └── css │ │ │ └── main.css │ ├── components │ │ ├── HeaderComponent.vue │ │ ├── LatestPost.vue │ │ ├── PagePlaceholder.vue │ │ ├── PostAside.vue │ │ ├── PostPlaceholder.vue │ │ ├── PrevNext.vue │ │ ├── UsePosts.vue │ │ ├── UsePostsByCategory.vue │ │ └── blocks │ │ │ └── CoreButton.vue │ ├── error.vue │ ├── layouts │ │ └── default.vue │ └── pages │ │ ├── [...slug].vue │ │ ├── index.vue │ │ └── test │ │ ├── composables.vue │ │ ├── config.vue │ │ └── errorHandling.vue ├── eslint.config.mjs ├── nuxt.config.ts ├── package.json ├── server │ └── tsconfig.json └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── src ├── context.ts ├── generate.ts ├── module.ts ├── runtime │ ├── components │ │ ├── ContentRenderer.vue │ │ ├── StagingBanner.vue │ │ ├── WPContent.vue │ │ ├── WPNuxtLogo.vue │ │ └── WordPressLogo.vue │ ├── composables │ │ ├── index.ts │ │ ├── isStaging.ts │ │ ├── useFeaturedImage.ts │ │ ├── usePrevNextPost.ts │ │ ├── useWPContent.ts │ │ └── useWPUri.ts │ ├── plugins │ │ └── vue-sanitize-directive.ts │ ├── queries │ │ ├── GeneralSettings.gql │ │ ├── Menu.gql │ │ ├── Node.gql │ │ ├── Pages.gql │ │ ├── Posts.gql │ │ ├── Revisions.gql │ │ ├── Viewer.gql │ │ └── fragments │ │ │ ├── ContentNode.fragment.gql │ │ │ ├── GeneralSettings.fragment.gql │ │ │ ├── MediaItem.fragment.gql │ │ │ ├── MenuItem.fragment.gql │ │ │ ├── NodeWithContentEditor.fragment.gql │ │ │ ├── NodeWithExcerpt.fragment.gql │ │ │ ├── NodeWithFeaturedImage.fragment.gql │ │ │ ├── NodeWithFeaturedImageToMediaItemConnectionEdge.fragment.gql │ │ │ ├── Page.fragment.gql │ │ │ └── Post.fragment.gql │ ├── server │ │ ├── api │ │ │ ├── config.ts │ │ │ └── wpContent.post.ts │ │ └── index.ts │ └── util │ │ ├── images.ts │ │ └── logger.ts ├── types.d.ts ├── useParser.ts └── utils.ts ├── tests ├── basic.test.ts └── fixtures │ └── basic │ ├── nuxt.config.ts │ ├── package.json │ └── pages │ └── index.vue ├── tsconfig.json └── wordpress ├── demo-content ├── newmodule.txt ├── testpage.txt └── testpost.txt ├── wp-env-apache-modules.yml └── wp-env-cli.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 2 5 | indent_style = space 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>nuxt/renovate-config-nuxt" 5 | ], 6 | "assignees": [ 7 | "vernaillen" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci-main 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - "docs/**" 7 | - "*.md" 8 | branches: 9 | - main 10 | pull_request: 11 | paths-ignore: 12 | - "docs/**" 13 | - "*.md" 14 | branches: 15 | - main 16 | 17 | jobs: 18 | ci: 19 | runs-on: ubuntu-latest 20 | 21 | env: 22 | NUXT_UI_PRO_LICENSE: ${{ secrets.NUXT_UI_PRO_LICENSE }} 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - uses: pnpm/action-setup@v4 28 | name: Install pnpm 29 | id: pnpm-install 30 | with: 31 | run_install: false 32 | version: 9 33 | 34 | - name: Install dependencies 35 | run: pnpm install 36 | 37 | - name: Install dependencies 38 | run: pnpm install 39 | 40 | - name: Lint 41 | run: pnpm run lint 42 | 43 | - name: Module Build 44 | run: pnpm run build 45 | 46 | - name: Playground Build 47 | run: pnpm run dev:build 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | 4 | # Logs 5 | *.log* 6 | 7 | # Temp directories 8 | .temp 9 | .tmp 10 | .cache 11 | 12 | # Yarn 13 | **/.yarn/cache 14 | **/.yarn/*state* 15 | 16 | # Generated dirs 17 | dist 18 | 19 | # Nuxt 20 | .nuxt 21 | .output 22 | .data 23 | .vercel_build_output 24 | .build-* 25 | .netlify 26 | 27 | # Env 28 | .env 29 | 30 | # Testing 31 | reports 32 | coverage 33 | *.lcov 34 | .nyc_output 35 | 36 | # VSCode 37 | .vscode/* 38 | !.vscode/settings.json 39 | !.vscode/tasks.json 40 | !.vscode/launch.json 41 | !.vscode/extensions.json 42 | !.vscode/*.code-snippets 43 | 44 | # Intellij idea 45 | *.iml 46 | .idea 47 | 48 | # OSX 49 | .DS_Store 50 | .AppleDouble 51 | .LSOverride 52 | .AppleDB 53 | .AppleDesktop 54 | Network Trash Folder 55 | Temporary Items 56 | .apdisk 57 | 58 | 59 | playground/.vercel/ 60 | wordpress/files/ 61 | playground/.queries/ 62 | playground/queries/ 63 | playground/schema.graphql 64 | tests/fixtures/basic/.queries/ 65 | tests/fixtures/basic/queries/ 66 | tests/fixtures/basic/schema.graphql 67 | playground/.eslintcache 68 | docs/.eslintcache 69 | .queries 70 | schema.graphql 71 | playground/package-lock.json 72 | tests/fixtures/basic/package-lock.json 73 | -------------------------------------------------------------------------------- /.markdownlint.yml: -------------------------------------------------------------------------------- 1 | # Default state for all rules 2 | default: true 3 | # Disable max line length 4 | MD013: false 5 | # Allow duplicated heading for different sections 6 | MD024: 7 | allow_different_nesting: true 8 | siblings_only: true 9 | # Allow multiple top-level headings 10 | MD025: false 11 | # Allow inline HTML 12 | MD033: false 13 | # Allow non blank lines around list 14 | MD032: false 15 | MD046: 16 | style: fenced 17 | MD034: false 18 | MD031: false 19 | MD007: false 20 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true -------------------------------------------------------------------------------- /.nuxtrc: -------------------------------------------------------------------------------- 1 | imports.autoImport=false 2 | typescript.includeWorkspace=true 3 | typescript.tsConfig.compilerOptions.noUncheckedIndexedAccess=true 4 | -------------------------------------------------------------------------------- /.release-it-edge.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "push": true 4 | }, 5 | "github": { 6 | "release": true, 7 | "releaseName": "v${version}" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "commitMessage": "chore(release): release v${version}" 4 | }, 5 | "github": { 6 | "release": true, 7 | "releaseName": "v${version}" 8 | }, 9 | "hooks": { 10 | "after:bump": "npx changelogen@latest --no-commit --no-tag --output --r $(node -p \"require('./package.json').version\")" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Enable ESlint flat config support 3 | "eslint.experimental.useFlatConfig": true, 4 | } 5 | -------------------------------------------------------------------------------- /.wp-env.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "https://github.com/wpnuxt/wpnuxt-plugin/releases/download/v0.0.3/wpnuxt-plugin.zip", 4 | "https://downloads.wordpress.org/plugin/faustwp.zip", 5 | "https://downloads.wordpress.org/plugin/advanced-custom-fields.zip", 6 | "https://downloads.wordpress.org/plugin/wp-graphql.zip", 7 | "https://downloads.wordpress.org/plugin/wpgraphql-acf.zip", 8 | "https://github.com/wpengine/wp-graphql-content-blocks/releases/download/v4.8.3/wp-graphql-content-blocks.zip" 9 | ], 10 | "port": 4000, 11 | "config": { 12 | "FRONTEND_URI": "http://localhost:3000" 13 | }, 14 | "mappings": { 15 | "wp-cli.yml": "./wordpress/wp-env-apache-modules.yml", 16 | "demo-content": "./wordpress/demo-content" 17 | }, 18 | "lifecycleScripts": { 19 | "afterStart": "wp-env run cli wp rewrite structure /%postname%/ --hard" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## v1.0.0-edge.12 5 | 6 | [compare changes](https://github.com/wpnuxt/wpnuxt-core/compare/v1.0.0-edge.11...v1.0.0-edge.12) 7 | 8 | ### 🏡 Chore 9 | 10 | - Update dependencies - upgrade to nuxt 3.13.0 ([5d9e0cc](https://github.com/wpnuxt/wpnuxt-core/commit/5d9e0cc)) 11 | 12 | ### ❤️ Contributors 13 | 14 | - Wouter Vernaillen 15 | 16 | ## v0.5.8 17 | 18 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.5.7...v0.5.8) 19 | 20 | ## v0.5.7 21 | 22 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.5.6...v0.5.7) 23 | 24 | ## v0.5.6 25 | 26 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.5.5...v0.5.6) 27 | 28 | ### 🏡 Chore 29 | 30 | - Update dependencies - ([705650a](https://github.com/vernaillen/wpnuxt-module/commit/705650a)) 31 | - Update dependencies - nuxt-graphql-middleware 4.1.1 ([fd47665](https://github.com/vernaillen/wpnuxt-module/commit/fd47665)) 32 | - Update dependencies - ([29d66d0](https://github.com/vernaillen/wpnuxt-module/commit/29d66d0)) 33 | - Update dependencies - ([d717e0e](https://github.com/vernaillen/wpnuxt-module/commit/d717e0e)) 34 | - Update dependencies - ([ce3c1ee](https://github.com/vernaillen/wpnuxt-module/commit/ce3c1ee)) 35 | 36 | ### ❤️ Contributors 37 | 38 | - Wouter Vernaillen 39 | 40 | ## v0.5.5 41 | 42 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.5.4...v0.5.5) 43 | 44 | ## v0.5.4 45 | 46 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.5.3...v0.5.4) 47 | 48 | ## v0.5.3 49 | 50 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.5.2...v0.5.3) 51 | 52 | ## v0.5.2 53 | 54 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.5.1...v0.5.2) 55 | 56 | ## v0.5.1 57 | 58 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.5.0...v0.5.1) 59 | 60 | ## v0.5.0 61 | 62 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.4.4...v0.5.0) 63 | 64 | ## v0.4.4 65 | 66 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.4.3...v0.4.4) 67 | 68 | ### 🏡 Chore 69 | 70 | * release-it config in separate files (0c1798f) 71 | * chore(deps): update nuxt framework to ^3.12.2 (#127) (16b0633) 72 | * Merge branch 'main' of github.com:vernaillen/wpnuxt-module (1259f1d) 73 | * chore(deps): update devdependency @nuxthq/studio to v2 (#128) (e6b1cdc) 74 | * chore(deps): update all non-major dependencies (#126) (dfec370) 75 | * chore(deps): Nuxt 3.12.1 (8b61270) 76 | 77 | ### ❤️ Contributors 78 | 79 | - Wouter Vernaillen 80 | 81 | ## v0.4.3 82 | 83 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.4.2...v0.4.3) 84 | 85 | ## v0.4.2 86 | 87 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.4.1...v0.4.2) 88 | 89 | ## v0.4.1 90 | 91 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.4.0...v0.4.1) 92 | 93 | ## v0.4.0 94 | 95 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.3.11...v0.4.0) 96 | 97 | ## v0.3.11 98 | 99 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.3.10...v0.3.11) 100 | 101 | ## v0.3.10 102 | 103 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.3.9...v0.3.10) 104 | 105 | ## v0.3.9 106 | 107 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.3.8...v0.3.9) 108 | 109 | ### 🏡 Chore 110 | 111 | - **release:** V0.3.8 ([d508794](https://github.com/vernaillen/wpnuxt-module/commit/d508794)) 112 | 113 | ### ❤️ Contributors 114 | 115 | - Wouter Vernaillen 116 | 117 | ## v0.3.8 118 | 119 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.3.7...v0.3.8) 120 | 121 | ### 🏡 Chore 122 | 123 | - **release:** V0.3.7 ([695df6a](https://github.com/vernaillen/wpnuxt-module/commit/695df6a)) 124 | 125 | ### ❤️ Contributors 126 | 127 | - Wouter Vernaillen 128 | 129 | ## v0.3.7 130 | 131 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.3.6...v0.3.7) 132 | 133 | ### 🏡 Chore 134 | 135 | - **release:** V0.3.6 ([bada49d](https://github.com/vernaillen/wpnuxt-module/commit/bada49d)) 136 | 137 | ### ❤️ Contributors 138 | 139 | - Wouter Vernaillen 140 | 141 | ## v0.3.6 142 | 143 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.3.5...v0.3.6) 144 | 145 | ### 🏡 Chore 146 | 147 | - **release:** V0.3.5 ([8ff0720](https://github.com/vernaillen/wpnuxt-module/commit/8ff0720)) 148 | 149 | ### ❤️ Contributors 150 | 151 | - Wouter Vernaillen 152 | 153 | ## v0.3.5 154 | 155 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.3.4...v0.3.5) 156 | 157 | ### 🏡 Chore 158 | 159 | - **release:** V0.3.4 ([d9768ba](https://github.com/vernaillen/wpnuxt-module/commit/d9768ba)) 160 | 161 | ### ❤️ Contributors 162 | 163 | - Wouter Vernaillen 164 | 165 | ## v0.3.4 166 | 167 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.3.2...v0.3.4) 168 | 169 | ## v0.3.3 170 | 171 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.3.2...v0.3.3) 172 | 173 | ## v0.3.2 174 | 175 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.3.1...v0.3.2) 176 | 177 | ## v0.3.1 178 | 179 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.22...v0.3.1) 180 | 181 | ## v0.3.0 182 | 183 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.28...v0.3.0) 184 | 185 | ### 🏡 Chore 186 | 187 | - **release:** V0.2.28 ([c0efcab](https://github.com/vernaillen/wpnuxt-module/commit/c0efcab)) 188 | 189 | ### ❤️ Contributors 190 | 191 | - Wouter Vernaillen 192 | 193 | ## v0.2.28 194 | 195 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.28...v0.2.28) 196 | 197 | ## v0.2.28 198 | 199 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.27...v0.2.28) 200 | 201 | ## v0.2.27 202 | 203 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.26...v0.2.27) 204 | 205 | ## v0.2.26 206 | 207 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.25...v0.2.26) 208 | 209 | ## v0.2.25 210 | 211 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.24...v0.2.25) 212 | 213 | ## v0.2.24 214 | 215 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.23...v0.2.24) 216 | 217 | ## v0.2.23 218 | 219 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.22...v0.2.23) 220 | 221 | ## v0.2.22 222 | 223 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.21...v0.2.22) 224 | 225 | ### 🏡 Chore 226 | 227 | - **release:** V0.2.21 ([6d1460c](https://github.com/vernaillen/wpnuxt-module/commit/6d1460c)) 228 | 229 | ### ❤️ Contributors 230 | 231 | - Wouter Vernaillen 232 | 233 | ## v0.2.21 234 | 235 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.20...v0.2.21) 236 | 237 | ## v0.2.20 238 | 239 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.19...v0.2.20) 240 | 241 | ## v0.2.19 242 | 243 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.18...v0.2.19) 244 | 245 | ## v0.2.18 246 | 247 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.17...v0.2.18) 248 | 249 | ## v0.2.17 250 | 251 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.16...v0.2.17) 252 | 253 | ### 🏡 Chore 254 | 255 | - **release:** V0.2.16 ([d1c0b04](https://github.com/vernaillen/wpnuxt-module/commit/d1c0b04)) 256 | 257 | ### ❤️ Contributors 258 | 259 | - Wouter Vernaillen 260 | 261 | ## v0.2.16 262 | 263 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.15...v0.2.16) 264 | 265 | ### 🏡 Chore 266 | 267 | - **release:** V0.2.15 ([d929485](https://github.com/vernaillen/wpnuxt-module/commit/d929485)) 268 | 269 | ### ❤️ Contributors 270 | 271 | - Wouter Vernaillen 272 | 273 | ## v0.2.15 274 | 275 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.14...v0.2.15) 276 | 277 | ### 🏡 Chore 278 | 279 | - **release:** V0.2.14 ([b65afb5](https://github.com/vernaillen/wpnuxt-module/commit/b65afb5)) 280 | 281 | ### ❤️ Contributors 282 | 283 | - Wouter Vernaillen 284 | 285 | ## v0.2.14 286 | 287 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.13...v0.2.14) 288 | 289 | ### 🏡 Chore 290 | 291 | - **release:** V0.2.13 ([765ab34](https://github.com/vernaillen/wpnuxt-module/commit/765ab34)) 292 | 293 | ### ❤️ Contributors 294 | 295 | - Wouter Vernaillen 296 | 297 | ## v0.2.13 298 | 299 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.12...v0.2.13) 300 | 301 | ### 🏡 Chore 302 | 303 | - **release:** V0.2.12 ([d9cd094](https://github.com/vernaillen/wpnuxt-module/commit/d9cd094)) 304 | 305 | ### ❤️ Contributors 306 | 307 | - Wouter Vernaillen 308 | 309 | ## v0.2.12 310 | 311 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.11...v0.2.12) 312 | 313 | ## v0.2.11 314 | 315 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.10...v0.2.11) 316 | 317 | ### 🏡 Chore 318 | 319 | - Package updates nuxt 3.11.1 ([52588c2](https://github.com/vernaillen/wpnuxt-module/commit/52588c2)) 320 | - Package updates regenerated lock file ([9434549](https://github.com/vernaillen/wpnuxt-module/commit/9434549)) 321 | 322 | ### ❤️ Contributors 323 | 324 | - Wouter Vernaillen 325 | 326 | ## v0.2.10 327 | 328 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.9...v0.2.10) 329 | 330 | ### 🏡 Chore 331 | 332 | - Package updates ([003a7b4](https://github.com/vernaillen/wpnuxt-module/commit/003a7b4)) 333 | - Package updates nuxt 3.11.0 ([4c88971](https://github.com/vernaillen/wpnuxt-module/commit/4c88971)) 334 | 335 | ### ❤️ Contributors 336 | 337 | - Wouter Vernaillen 338 | 339 | ## v0.2.9 340 | 341 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.8...v0.2.9) 342 | 343 | ### 🏡 Chore 344 | 345 | - **release:** V0.2.8 ([8b17da0](https://github.com/vernaillen/wpnuxt-module/commit/8b17da0)) 346 | - Package updates ([bc729dd](https://github.com/vernaillen/wpnuxt-module/commit/bc729dd)) 347 | 348 | ### ❤️ Contributors 349 | 350 | - Wouter Vernaillen 351 | 352 | ## v0.2.8 353 | 354 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.7...v0.2.8) 355 | 356 | ## v0.2.7 357 | 358 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.6...v0.2.7) 359 | 360 | ### 🏡 Chore 361 | 362 | - **release:** V0.2.6 ([2e3314d](https://github.com/vernaillen/wpnuxt-module/commit/2e3314d)) 363 | 364 | ### ❤️ Contributors 365 | 366 | - Wouter Vernaillen 367 | 368 | ## v0.2.6 369 | 370 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.6...v0.2.6) 371 | 372 | ## v0.2.5 373 | 374 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.4...v0.2.5) 375 | 376 | ## v0.2.4 377 | 378 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.3...v0.2.4) 379 | 380 | ## v0.2.3 381 | 382 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.2...v0.2.3) 383 | 384 | ## v0.2.2 385 | 386 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.1...v0.2.2) 387 | 388 | ## v0.2.1 389 | 390 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.2.0...v0.2.1) 391 | 392 | ## v0.2.0 393 | 394 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.32...v0.2.0) 395 | 396 | ## v0.1.32 397 | 398 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.31...v0.1.32) 399 | 400 | ## v0.1.31 401 | 402 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.30...v0.1.31) 403 | 404 | ### 🏡 Chore 405 | 406 | - **release:** V0.1.30 ([3ed91e7](https://github.com/vernaillen/wpnuxt-module/commit/3ed91e7)) 407 | 408 | ### ❤️ Contributors 409 | 410 | - Wouter Vernaillen 411 | 412 | ## v0.1.30 413 | 414 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.29...v0.1.30) 415 | 416 | ## v0.1.29 417 | 418 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.28...v0.1.29) 419 | 420 | ### 🏡 Chore 421 | 422 | - Package updates ([4ceb2d7](https://github.com/vernaillen/wpnuxt-module/commit/4ceb2d7)) 423 | - Package updates ([5460777](https://github.com/vernaillen/wpnuxt-module/commit/5460777)) 424 | - Package updates ([89068ac](https://github.com/vernaillen/wpnuxt-module/commit/89068ac)) 425 | - Package updates ([a5cfe41](https://github.com/vernaillen/wpnuxt-module/commit/a5cfe41)) 426 | 427 | ### ❤️ Contributors 428 | 429 | - Wouter Vernaillen 430 | 431 | ## v0.1.28 432 | 433 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.27...v0.1.28) 434 | 435 | ### 🏡 Chore 436 | 437 | - Package updates ([f74626f](https://github.com/vernaillen/wpnuxt-module/commit/f74626f)) 438 | - Package updates ([a9d94c8](https://github.com/vernaillen/wpnuxt-module/commit/a9d94c8)) 439 | 440 | ### ❤️ Contributors 441 | 442 | - Wouter Vernaillen 443 | 444 | ## v0.1.27 445 | 446 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.26...v0.1.27) 447 | 448 | ### 🏡 Chore 449 | 450 | - **release:** V0.1.26 ([f955f6e](https://github.com/vernaillen/wpnuxt-module/commit/f955f6e)) 451 | 452 | ### ❤️ Contributors 453 | 454 | - Wouter Vernaillen 455 | 456 | ## v0.1.26 457 | 458 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.25...v0.1.26) 459 | 460 | ## v0.1.25 461 | 462 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.24...v0.1.25) 463 | 464 | ### 🏡 Chore 465 | 466 | - Package updates ([c6bdcd9](https://github.com/vernaillen/wpnuxt-module/commit/c6bdcd9)) 467 | - Package updates ([b24b555](https://github.com/vernaillen/wpnuxt-module/commit/b24b555)) 468 | 469 | ### ❤️ Contributors 470 | 471 | - Wouter Vernaillen 472 | 473 | ## v0.1.24 474 | 475 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.23...v0.1.24) 476 | 477 | ### 🏡 Chore 478 | 479 | - Package updates ([745ecac](https://github.com/vernaillen/wpnuxt-module/commit/745ecac)) 480 | 481 | ### ❤️ Contributors 482 | 483 | - Wouter Vernaillen 484 | 485 | ## v0.1.23 486 | 487 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.22...v0.1.23) 488 | 489 | ## v0.1.22 490 | 491 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.21...v0.1.22) 492 | 493 | ## v0.1.21 494 | 495 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.20...v0.1.21) 496 | 497 | ## v0.1.20 498 | 499 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.19...v0.1.20) 500 | 501 | ## v0.1.19 502 | 503 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.18...v0.1.19) 504 | 505 | ## v0.1.18 506 | 507 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.17...v0.1.18) 508 | 509 | ## v0.1.17 510 | 511 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.16...v0.1.17) 512 | 513 | ## v0.1.16 514 | 515 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.15...v0.1.16) 516 | 517 | ## v0.1.15 518 | 519 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.14...v0.1.15) 520 | 521 | ### 🏡 Chore 522 | 523 | - Package updates ([e5b07dc](https://github.com/vernaillen/wpnuxt-module/commit/e5b07dc)) 524 | 525 | ### ❤️ Contributors 526 | 527 | - Wouter Vernaillen 528 | 529 | ## v0.1.14 530 | 531 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.13...v0.1.14) 532 | 533 | ### 🏡 Chore 534 | 535 | - **release:** V0.1.13 ([7c53dea](https://github.com/vernaillen/wpnuxt-module/commit/7c53dea)) 536 | - Package updates ([3791437](https://github.com/vernaillen/wpnuxt-module/commit/3791437)) 537 | 538 | ### ❤️ Contributors 539 | 540 | - Wouter Vernaillen 541 | 542 | ## v0.1.13 543 | 544 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.12...v0.1.13) 545 | 546 | ### 🏡 Chore 547 | 548 | - Package updates ([5ce5e11](https://github.com/vernaillen/wpnuxt-module/commit/5ce5e11)) 549 | 550 | ### ❤️ Contributors 551 | 552 | - Wouter Vernaillen 553 | 554 | ## v0.1.12 555 | 556 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.11...v0.1.12) 557 | 558 | ## v0.1.11 559 | 560 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.10...v0.1.11) 561 | 562 | ## v0.1.10 563 | 564 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.9...v0.1.10) 565 | 566 | ## v0.1.9 567 | 568 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.8...v0.1.9) 569 | 570 | ### 🏡 Chore 571 | 572 | - Package updates ([97e7546](https://github.com/vernaillen/wpnuxt-module/commit/97e7546)) 573 | 574 | ### ❤️ Contributors 575 | 576 | - Wouter Vernaillen 577 | 578 | ## v0.1.8 579 | 580 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.7...v0.1.8) 581 | 582 | ### 🏡 Chore 583 | 584 | - **release:** V0.1.7 ([69ca6ba](https://github.com/vernaillen/wpnuxt-module/commit/69ca6ba)) 585 | - Package updates ([d11a96a](https://github.com/vernaillen/wpnuxt-module/commit/d11a96a)) 586 | - Package updates ([5559098](https://github.com/vernaillen/wpnuxt-module/commit/5559098)) 587 | - Package updates ([4ba2eba](https://github.com/vernaillen/wpnuxt-module/commit/4ba2eba)) 588 | - Package updates ([99f2d86](https://github.com/vernaillen/wpnuxt-module/commit/99f2d86)) 589 | - Package updates ([f526162](https://github.com/vernaillen/wpnuxt-module/commit/f526162)) 590 | 591 | ### ❤️ Contributors 592 | 593 | - Wouter Vernaillen 594 | 595 | ## v0.1.7 596 | 597 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.6...v0.1.7) 598 | 599 | ### 🏡 Chore 600 | 601 | - **release:** V0.1.6 ([ddceff1](https://github.com/vernaillen/wpnuxt-module/commit/ddceff1)) 602 | - Package updates ([4aa08f8](https://github.com/vernaillen/wpnuxt-module/commit/4aa08f8)) 603 | - Package updates ([7ea8957](https://github.com/vernaillen/wpnuxt-module/commit/7ea8957)) 604 | - Package updates ([029df6d](https://github.com/vernaillen/wpnuxt-module/commit/029df6d)) 605 | - Package updates ([2be87fa](https://github.com/vernaillen/wpnuxt-module/commit/2be87fa)) 606 | - Packages updates ([ef2e056](https://github.com/vernaillen/wpnuxt-module/commit/ef2e056)) 607 | 608 | ### ❤️ Contributors 609 | 610 | - Wouter Vernaillen 611 | 612 | ## v0.1.6 613 | 614 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.5...v0.1.6) 615 | 616 | ### 🏡 Chore 617 | 618 | - Package updates ([c7bb5f4](https://github.com/vernaillen/wpnuxt-module/commit/c7bb5f4)) 619 | 620 | ### ❤️ Contributors 621 | 622 | - Wouter Vernaillen 623 | 624 | ## v0.1.5 625 | 626 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.4...v0.1.5) 627 | 628 | ## v0.1.4 629 | 630 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.3...v0.1.4) 631 | 632 | ## v0.1.3 633 | 634 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.2...v0.1.3) 635 | 636 | ## v0.1.2 637 | 638 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.1.1...v0.1.2) 639 | 640 | ## v0.1.1 641 | 642 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.0.9...v0.1.1) 643 | 644 | ### 🏡 Chore 645 | 646 | - **release:** V0.0.9 ([ac53a81](https://github.com/vernaillen/wpnuxt-module/commit/ac53a81)) 647 | 648 | ### ❤️ Contributors 649 | 650 | - Wouter Vernaillen 651 | 652 | ## v0.0.9 653 | 654 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.0.9...v0.0.9) 655 | 656 | ## v0.0.9 657 | 658 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.0.8...v0.0.9) 659 | 660 | ## v0.0.8 661 | 662 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.0.7...v0.0.8) 663 | 664 | ## v0.0.7 665 | 666 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.0.6...v0.0.7) 667 | 668 | ## v0.0.6 669 | 670 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.0.5...v0.0.6) 671 | 672 | ## v0.0.5 673 | 674 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.0.4...v0.0.5) 675 | 676 | ### 🏡 Chore 677 | 678 | - **release:** V0.0.4 ([e88b4af](https://github.com/vernaillen/wpnuxt-module/commit/e88b4af)) 679 | - Package updates ([0d64539](https://github.com/vernaillen/wpnuxt-module/commit/0d64539)) 680 | 681 | ### ❤️ Contributors 682 | 683 | - Wouter Vernaillen 684 | 685 | ## v0.0.4 686 | 687 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.0.2...v0.0.4) 688 | 689 | ### 🏡 Chore 690 | 691 | - **release:** V0.0.2 ([da52837](https://github.com/vernaillen/wpnuxt-module/commit/da52837)) 692 | - **release:** V0.0.3 ([0e3af66](https://github.com/vernaillen/wpnuxt-module/commit/0e3af66)) 693 | - Package updates ([46c8ff4](https://github.com/vernaillen/wpnuxt-module/commit/46c8ff4)) 694 | - Package updates ([bf23f9f](https://github.com/vernaillen/wpnuxt-module/commit/bf23f9f)) 695 | - Package updates ([96c2035](https://github.com/vernaillen/wpnuxt-module/commit/96c2035)) 696 | - Package updates ([4fb51fe](https://github.com/vernaillen/wpnuxt-module/commit/4fb51fe)) 697 | 698 | ### ❤️ Contributors 699 | 700 | - Wouter Vernaillen 701 | 702 | ## v0.0.3 703 | 704 | [compare changes](https://github.com/vernaillen/wpnuxt-module/compare/v0.0.2...v0.0.3) 705 | 706 | ## v0.0.2 707 | 708 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024-present - WPNuxt Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | This module is still being developed and not ready for production usage yet. 3 | There will be many smaller alpha releases the coming weeks, often with breaking changes. 4 | 5 | I am working towards a stable release. And will inform about it here 6 | 7 | # WPNuxt 8 | 9 | [![npm version][npm-version-src]][npm-version-href] 10 | [![npm downloads][npm-downloads-src]][npm-downloads-href] 11 | [![License][license-src]][license-href] 12 | [![Nuxt][nuxt-src]][nuxt-href] 13 | 14 | Nuxt module for using WordPress as a headless CMS with a Nuxt 3 frontend 15 | 16 | - [✨  Release Notes](/CHANGELOG.md) 17 | - [🏀 Online playground](https://stackblitz.com/github/wpnuxt/wpnuxt-core?file=playground%2Fapp%2Fpages%2Findex.vue) 18 | - [📖  Documentation](https://wpnuxt.com) 19 | 20 | ## Features 21 | 22 | - Content is fetched from WordPress using server-side GraphQL api calls 23 | - Support for (Gutenberg Blocks) by adding the separate [@wpnuxt/blocks](https://github.com/wpnuxt/wpnuxt-blocks), which uses WPEngine's [wp-graphql-content-blocks](https://faustjs.org/tutorial/get-started-with-wp-graphql-content-blocks) and a set of custom vue components 24 | 25 | ## Quick Setup 26 | 27 | Install the module to your Nuxt application with one command: 28 | 29 | ```bash 30 | npx nuxi module add @wpnuxt/core 31 | ``` 32 | 33 | And connect WPNuxt to your wordpress installation in your nuxt.config.ts: 34 | 35 | ```json 36 | wpNuxt: { 37 | wordpressUrl: 'https://yourwordpress.domain.com' 38 | }, 39 | ``` 40 | 41 | That's it! You can now use the WPNuxt module in your Nuxt app ✨ 42 | 43 | ## Development 44 | 45 | ```bash 46 | # Install dependencies 47 | pnpm install 48 | 49 | # Generate type stubs 50 | pnpm run dev:prepare 51 | 52 | # Develop with the playground 53 | pnpm run dev 54 | 55 | # Build the playground 56 | pnpm run dev:build 57 | 58 | # Run ESLint 59 | pnpm run lint 60 | 61 | # Run Vitest 62 | pnpm run test 63 | pnpm run test:watch 64 | 65 | # Release new version 66 | pnpm run release 67 | ``` 68 | 69 | 70 | [npm-version-src]: https://img.shields.io/npm/v/@vernaillen/wpnuxt/latest.svg?style=flat&colorA=18181B&colorB=28CF8D 71 | [npm-version-href]: https://www.npmjs.com/package/@vernaillen/wpnuxt 72 | 73 | [npm-downloads-src]: https://img.shields.io/npm/dm/@vernaillen/wpnuxt.svg?style=flat&colorA=18181B&colorB=28CF8D 74 | [npm-downloads-href]: https://www.npmjs.com/package/@vernaillen/wpnuxt 75 | 76 | [license-src]: https://img.shields.io/npm/l/@vernaillen/wpnuxt?style=flat&colorA=18181B&colorB=28CF8D 77 | [license-href]: https://www.npmjs.com/package/@vernaillen/wpnuxt 78 | 79 | [nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js 80 | [nuxt-href]: https://nuxt.com 81 | -------------------------------------------------------------------------------- /docs/0.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'WPNuxt Core' 3 | description: What is @wpnuxt/core? 4 | icon: i-ph-package 5 | --- 6 | 7 | WPNuxt Core is a plugin for Nuxt 3 that provides a set of composables, hooks, and utilities for working with WordPress. 8 | 9 | It will render the content as is it returned by the WordPress GraphQL API. 10 | 11 | ## Gutenberg Blocks 12 | 13 | By default the GraphQL API returns the content of the page as an HTML string. When using WPNuxt Core this content is rendered as is, with the help of the [vue-sanitize-directive](https://github.com/leopiccionia/vue-sanitize-directive){target=_blank} to make sure we get clean HTML. 14 | 15 | Hovever, if you want to use and customise separate vue components to render Gutenberg blocks, you can use the [WPNuxt Blocks](/blocks) plugin. 16 | 17 | 18 | ## WPNuxt Modules + required WordPress plugin(s) 19 | 20 | | WPNuxt Module | Required WordPress Plugins | 21 | | ---------------- | ----------------------------- | 22 | | @wpnuxt/core | WPGraphQL | 23 | | @wpnuxt/blocks | WPGraphQL Content Blocks | 24 | | @wpnuxt/auth | WPGraphQL CORS | 25 | 26 | 27 | ideas for future modules: 28 | 29 | | @wpnuxt/yoastseo | Yoast SEO + Add WPGraphQL SEO | 30 | | @wpnuxt/wpml | WPML + WPML WPGraphql | 31 | | @wpnuxt/acf | ACF + WPGraphQL for ACF | 32 | -------------------------------------------------------------------------------- /docs/1.getting-started/1.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | description: Get started with WPNuxt by creating a new project or adding it to an existing Nuxt application. 4 | --- 5 | 6 | ## Try it online 7 | 8 | [open demo in StackBlitz](https://stackblitz.com/github/vernaillen/wpnuxt-demo) 9 | 10 | ## New Project 11 | 12 | ### Configure Wordpress 13 | 14 | First set up WordPress as a headless CMS, by installing these 3 plugins: 15 | * [WPGraphQL](https://wordpress.org/plugins/wp-graphql/) 16 | * WPEngine's [Faust.js](https://wordpress.org/plugins/faustwp/) 17 | * WPEngine's [WPGraphQL Content Blocks](https://github.com/wpengine/wp-graphql-content-blocks) (you'll need to download the latest release as a zip file from the [release page on github](https://github.com/wpengine/wp-graphql-content-blocks/releases) and install manually in wordpress) 18 | 19 | #### WordPress permalinks 20 | 21 | If you want to set up dynamic routes in Nuxt to match the WordPress slugs, then set the WordPress permalinks to postname. 22 | 23 | #### WPGraphQL settings 24 | 25 | * enable introspection 26 | 27 | #### Faust plugin 28 | 29 | * set the front-end site url 30 | * copy the secret key 31 | * depending on how you're going to handle image url's in Nuxt, you might have to enable "Use the WordPress domain for media URLs in post content" 32 | 33 | ### Add WPNuxt Core to your Nuxt app 34 | 35 | Install the module to your Nuxt application with one command: 36 | 37 | ```bash 38 | npx nuxi module add @wpnuxt/core 39 | ``` 40 | 41 | ### Configure WPNuxt 42 | 43 | Connect WPNuxt to your wordpress installation in your nuxt.config.ts: 44 | 45 | ```ts 46 | wpNuxt: { 47 | wordpressUrl: 'https://wordpress.wpnuxt.com' 48 | }, 49 | ``` 50 | Include the protocol. 51 | 52 | When you start Nuxt after adding the WPNuxt module, it might ask whether you want to install the @nuxt/image package: 53 | ``` bash 54 | ❯ Do you want to install @nuxt/image package? 55 | ● Yes / ○ No 56 | ``` 57 | 58 | More information about how to handle wordpress media: [images](./images) 59 | -------------------------------------------------------------------------------- /docs/1.getting-started/2.configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Configuration 3 | description: 4 | --- 5 | 6 | ```ts 7 | wpNuxt: { 8 | wordpressUrl: 'https://wordpress.wpnuxt.com', 9 | frontendUrl: 'https://demo.wpnuxt.com', 10 | composablesPrefix: 'useWP' 11 | defaultMenuName: 'main', 12 | enableCache: true, 13 | downloadSchema: true, 14 | logLevel: 3 15 | }, 16 | ``` 17 | 18 | ## wordpressUrl 19 | 20 | URL of the WordPress site 21 | 22 | There is no default value for this option, so it's always required. 23 | 24 | ## frontendUrl 25 | 26 | URL of the Nuxt app 27 | 28 | ## composablesPrefix 29 | 30 | ## defaultMenuName 31 | 32 | ## enableCache 33 | 34 | ## downloadSchema 35 | 36 | Downloads the GraphQL schema and store it on disk. 37 | 38 | When setting this to false, the module will expect a graphql.schema file to be available. 39 | 40 | You could first enable this, commit the schema file and then set downloadSchema to false to avoid have to do graphql introspection on each start of the application. This speeds up the build time of the application. 41 | 42 | ## logLevel 43 | 44 | -------------------------------------------------------------------------------- /docs/1.getting-started/3.composables.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Composables 3 | description: 4 | --- 5 | 6 | ## Composables 7 | 8 | Composables to fetch content are automatically generated by WPNuxt [based on predefined queries](../content-queries/posts) as well as your own [custom queries](../content-queries/custom-queries). 9 | 10 | There's also a few [other composables provided by WPNuxt](#other-composables-provided-by-wpnuxt) 11 | 12 | ### Based on Predefined queries 13 | 14 | [Check them here](https://github.com/wpnuxt/wpnuxt-core/tree/main/src/runtime/queries) 15 | 16 | Can be overwritten by providing a custom query with the same name. 17 | -------------------------------------------------------------------------------- /docs/1.getting-started/4.components.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Components 3 | description: 4 | --- 5 | 6 | ## WPContent 7 | 8 | ```vue 9 | 10 | ``` 11 | 12 | The node passed to the WPContent component must be of type NodeWithContentEditorFragment or NodeWithEditorBlocksFragment. 13 | -------------------------------------------------------------------------------- /docs/1.getting-started/5.authentication.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Authentication 3 | description: 4 | --- 5 | 6 | * relies on the Faust.js plugin 7 | * the Faust secret key (available in the Faust settings is WPAdmin) should be set in nuxt: 8 | 9 | in nuxt-config.ts: 10 | ```ts 11 | wpNuxt: { 12 | faustSecretKey: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 13 | } 14 | ``` 15 | 16 | or as env variable: 17 | ```properties 18 | WPNUXT_FAUST_SECRET_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/1.getting-started/6.images.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Images 3 | description: 4 | --- 5 | 6 | ### Configure Image Provider 7 | 8 | Since images are uploaded in WordPress, we can configure Nuxt Image to use an image provider that fetches the images from WordPress and optimises them. 9 | 10 | TODO: add configuration example 11 | 12 | ### wp-contennt proxy 13 | 14 | You can configure a proxy routeRule in Nuxt: 15 | 16 | ```ts 17 | routeRules: { 18 | '/wp-content/**': { proxy: { to: 'https://yourwordpress.com/wp-content/**' } } 19 | }, 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/1.getting-started/7.typescript.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Typescript 3 | description: 4 | --- 5 | 6 | Types are exported by the [Nuxt GraphQL Middleware plugin](https://nuxt-graphql-middleware.dulnan.net/) and can be imported with the [#graphql-operations](https://nuxt-graphql-middleware.dulnan.net/features/typescript.html#importing-types) alias. 7 | 8 | for example: 9 | ```ts 10 | import type { PostFragment, PageFragment } from '#graphql-operations' 11 | ``` 12 | -------------------------------------------------------------------------------- /docs/1.getting-started/_dir.yml: -------------------------------------------------------------------------------- 1 | title: Getting Started 2 | icon: i-ph-rocket-launch-duotone 3 | -------------------------------------------------------------------------------- /docs/2.composables/0.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Composables 3 | description: 4 | navigation: false 5 | --- 6 | 7 | ## Generated Composables based on provided queries 8 | 9 | * [Posts](/docs/composables/posts) 10 | * [Pages](/docs/composables/pages) 11 | * [Nodes](/docs/composables/nodes) 12 | * [Menu](/docs/composables/menu) 13 | * [Settings](/docs/composables/settings) 14 | * [Viewer](/docs/composables/viewer) 15 | 16 | ## Generated Composables based on your custom queries 17 | 18 | * [Custom Queries](/docs/composables/custom-queries) 19 | 20 | ## Other Composables 21 | 22 | * [Staging](/docs/composables/staging) 23 | * [PreviousNextPost](/docs/composables/previous-next-post) 24 | -------------------------------------------------------------------------------- /docs/2.composables/1.posts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Posts 3 | description: Generated composables to fetch Posts and their content 4 | --- 5 | 6 | WPNuxt provides 3 GraphQL queries to fetch posts ([src](https://github.com/wpnuxt/wpnuxt-core/blob/main/src/runtime/queries/Posts.gql){target=_blank}): 7 | 8 | ::code-group 9 | ```graphql [GraphQL queries] 10 | #import "~/.queries/fragments/Post.fragment.gql" 11 | 12 | query Posts($limit: Int = 10) { 13 | posts(first: $limit) { 14 | nodes { 15 | ...Post 16 | } 17 | } 18 | } 19 | query PostByUri($uri: String!) { 20 | nodeByUri(uri: $uri) { 21 | ...Post 22 | } 23 | } 24 | query PostById($id: ID!, $asPreview: Boolean = false) { 25 | post(id: $id, idType: DATABASE_ID, asPreview: $asPreview) { 26 | ...Post 27 | } 28 | } 29 | ``` 30 | 31 | ```graphql [GraphQL fragment] 32 | #import "~/.queries/fragments/NodeWithExcerpt.fragment.gql"; 33 | #import '~/.queries/fragments/ContentNode.fragment.gql'; 34 | #import '~/.queries/fragments/NodeWithFeaturedImage.fragment.gql'; 35 | #import '~/.queries/fragments/CoreGallery.fragment.gql'; 36 | #import '~/.queries/fragments/CoreImage.fragment.gql'; 37 | #import '~/.queries/fragments/CoreParagraph.fragment.gql'; 38 | #import '~/.queries/fragments/CoreQuote.fragment.gql'; 39 | #import '~/.queries/fragments/EditorBlock.fragment.gql'; 40 | 41 | fragment Post on Post { 42 | ...NodeWithExcerpt 43 | ...ContentNode 44 | ...NodeWithFeaturedImage 45 | content 46 | title 47 | editorBlocks { 48 | ...EditorBlock 49 | innerBlocks { 50 | ...EditorBlock 51 | } 52 | } 53 | } 54 | ``` 55 | :: 56 | 57 | Which result in these generated PostFragment type and 3 composables: 58 | 59 | ```ts twoslash 60 | const { data: allPosts } = await useWPPosts() 61 | const { data: latestPosts } = await useWPPosts({ limit: 3 }) 62 | const { data: post1 } = await useWPPostByUri({ uri: 'slug' }) 63 | const { data: post2 } = await useWPPostById({ id: 'databaseId' }) 64 | ``` 65 | -------------------------------------------------------------------------------- /docs/2.composables/2.pages.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pages 3 | description: Generated composables to fetch Pages and their content 4 | --- 5 | 6 | WPNuxt provides 3 [queries to fetch pages](https://github.com/wpnuxt/wpnuxt-core/blob/main/src/runtime/queries/Pages.gql), which result in these generated composables: 7 | 8 | ```ts twoslash 9 | const { data: pages } = await useWPPages() 10 | const { data: page1 } = await useWPPageById({ id: 'databaseId' }) 11 | const { data: page2 } = await useWPPageByUri({ uri: 'slug' }) 12 | ``` 13 | -------------------------------------------------------------------------------- /docs/2.composables/3.nodes.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Nodes 3 | description: Generated composables to fetch Nodes and their content 4 | --- 5 | 6 | #### useNodeByUri 7 | 8 | a usefull composable if you want to fetch content by uri, regardless whether it's a page or post type in WordPress. 9 | 10 | ```ts twoslash 11 | const { data: node } = await useWPNodeByUri({ uri: 'slug' }) 12 | ``` 13 | 14 | an example how to use this for a \[...slug\].vue page: 15 | ```ts twoslash 16 | const route = useRoute() 17 | if (route.params?.slug && route.params.slug[0]) { 18 | const { data: currentNode } = await useWPNodeByUri({ uri: route.params.slug[0] }) 19 | } 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/2.composables/4.menu.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Menus 3 | description: Generated composables to fetch Menus 4 | --- 5 | 6 | ```ts twoslash 7 | const { data: mainMenu } = useWPMenu() 8 | const { data: footerMenu } = await useWPMenu({ name: 'footer' }) 9 | ``` 10 | if you don't specify a menu name, then WPNuxt will try to fetch the menu with name "main" 11 | -------------------------------------------------------------------------------- /docs/2.composables/5.settings.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Settings 3 | description: Generated composables to fetch Settings 4 | --- 5 | 6 | ```ts twoslash 7 | const { data: settings } = await useWPGeneralSettings() 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/2.composables/6.viewer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Viewer 3 | description: Generated composables to fetch Viewer 4 | --- 5 | 6 | ```ts twoslash 7 | const { data: viewer } = await useWPViewer() 8 | ``` 9 | Authentication is required to fetch the Viewer 10 | -------------------------------------------------------------------------------- /docs/2.composables/7.custom-queries.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Custom Queries 3 | description: 4 | --- 5 | 6 | Create movies.gql and put it in /extend/queries folder in your project 7 | 8 | ```gql 9 | query Movies($limit: Int = 10) { 10 | movies(first: $limit) { 11 | nodes { 12 | ...Movie 13 | } 14 | } 15 | } 16 | fragment Movie on Movie { 17 | ...ContentNode 18 | ...NodeWithFeaturedImage 19 | content 20 | title 21 | editorBlocks { 22 | name 23 | } 24 | } 25 | ``` 26 | 27 | Based on the above infomation WPNuxt will generate: 28 | * the type 'MovieFragment' 29 | * the composable 'useMovies': 30 | * input params: { limit: integer } 31 | * return: MovieFragment[] 32 | -------------------------------------------------------------------------------- /docs/2.composables/8.staging.md: -------------------------------------------------------------------------------- 1 | 2 | #### Staging 3 | 4 | ```ts twoslash 5 | const staging = await isStaging() 6 | ``` 7 | More about this on the [staging](../3.advanced/4.staging.md) page. 8 | -------------------------------------------------------------------------------- /docs/2.composables/9.previous-next-post.md: -------------------------------------------------------------------------------- 1 | 2 | #### get Previous/Next posts 3 | 4 | ```ts twoslash [usePrevNextPost()] 5 | const { prev: prev, next: next } = await usePrevNextPost('currentPostSlug') 6 | ``` 7 | 8 | An example how to use the composable: 9 | 10 | ```vue [[...uri.vue\] ] 11 | 15 | 16 | 38 | ``` 39 | 40 | -------------------------------------------------------------------------------- /docs/2.composables/_dir.yml: -------------------------------------------------------------------------------- 1 | title: Composables 2 | icon: i-carbon-cloud-download 3 | -------------------------------------------------------------------------------- /docs/3.other-composables/_dir.yml: -------------------------------------------------------------------------------- 1 | title: Other Composables 2 | icon: i-carbon-cloud-services 3 | -------------------------------------------------------------------------------- /docs/4.advanced/1.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Custom Post Types 3 | description: 4 | --- 5 | 6 | ## Create CPT in WordPress 7 | 8 | [WordPress documentation about post types](https://developer.wordpress.org/reference/functions/register_post_type/) 9 | 10 | In te first part of this [Tutorial by Faust](https://faustjs.org/guide/setting-up-custom-post-types-cpts-in-faust) they guide you through the steps to set up your CPT 11 | 12 | ## Check query in GraphiQl IDE 13 | 14 | ## Add the query in your project 15 | 16 | Create movies.gql and put it in /extend/queries folder in your project 17 | 18 | ```graphql 19 | query Movies($limit: Int = 10) { 20 | movies(first: $limit) { 21 | nodes { 22 | ...Movie 23 | } 24 | } 25 | } 26 | fragment Movie on Movie { 27 | ...ContentNode 28 | ...NodeWithFeaturedImage 29 | content 30 | title 31 | editorBlocks { 32 | name 33 | } 34 | } 35 | ``` 36 | 37 | Based on the above infomation WPNuxt will generate: 38 | * the type 'MovieFragment' 39 | * the composable 'useMovies': 40 | * input params: { limit: integer } 41 | * return: MovieFragment[] 42 | -------------------------------------------------------------------------------- /docs/4.advanced/2.extending-posts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Extending Posts & Pages 3 | description: 4 | --- 5 | 6 | ## Example: Yoast SEO 7 | 8 | coming soon 9 | -------------------------------------------------------------------------------- /docs/4.advanced/3.customcomponents.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Create Block Components 3 | description: how to create your own Nuxt Components to render Gutenberg Blocks 4 | --- 5 | 6 | By default every GutenBerg block will be rendered with the WPNuxt component EditorBlock, which is merely outputting the renderedHtml in a p tag: 7 | 8 | ``` vue 9 | 16 | 17 | 20 | ``` 21 | 22 | ## Add your own components 23 | 24 | WPNuxt allows you to add a Nuxt component to render any Gutenberg block by merely creating a component with the name of the Gutenberg block 25 | 26 | Example component to render a CoreButton block using a Nuxt UI button: 27 | ``` vue 28 | 35 | 36 | 42 | ``` 43 | 44 | More examples can be found in the [WPnuxt demo](https://github.com/vernaillen/wpnuxt-demo/tree/main/app/components/blocks) 45 | -------------------------------------------------------------------------------- /docs/4.advanced/4.staging.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Staging 3 | description: 4 | --- 5 | 6 | coming soon 7 | -------------------------------------------------------------------------------- /docs/4.advanced/_dir.yml: -------------------------------------------------------------------------------- 1 | title: Advanced 2 | icon: i-oui-app-advanced-settings 3 | -------------------------------------------------------------------------------- /docs/5.localdev/1.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: wp-env 3 | description: 4 | --- 5 | 6 | coming soon 7 | 8 | [Get started with wp-env](https://developer.wordpress.org/block-editor/getting-started/devenv/get-started-with-wp-env/) 9 | -------------------------------------------------------------------------------- /docs/5.localdev/2.docker-compose.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Docker-compose 3 | description: use docker-compose to run a local WordPress instance on your computer 4 | --- 5 | 6 | coming soon 7 | -------------------------------------------------------------------------------- /docs/5.localdev/3.localwp.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: LocalWP 3 | description: use LocalWP to run a local WordPress instance on your computer 4 | --- 5 | 6 | coming soon 7 | -------------------------------------------------------------------------------- /docs/5.localdev/_dir.yml: -------------------------------------------------------------------------------- 1 | title: Local Development Setup 2 | icon: i-material-symbols-code 3 | -------------------------------------------------------------------------------- /docs/6.under-the-hood/1.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Dependencies 3 | description: 4 | --- 5 | 6 | ## Nuxt GraphQL Middleware 7 | 8 | The [Nuxt GraphQL Middleware](https://github.com/dulnan/nuxt-graphql-middleware) by Jan Hug, [Dulnan on GitHub](https://github.com/dulnan) is used to fetch data from the [WPGraphQL endpoints](https://www.wpgraphql.com/). 9 | 10 | ## @graphql-codegen 11 | 12 | ## Nuxt Image 13 | -------------------------------------------------------------------------------- /docs/6.under-the-hood/2.code-generation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Code Generation 3 | description: 4 | --- 5 | 6 | Composables are automatically generated by WPNuxt based on [predefined queries](https://github.com/wpnuxt/wpnuxt-core/tree/main/src/runtime/queries) as well as your own [custom queries](../advanced/custom-post-types.md). 7 | 8 | This is done with the help of [GraphQL-Codegen](https://the-guild.dev/graphql/codegen) 9 | 10 | # Composables 11 | 12 | Composables are generated based on the given queries. 13 | 14 | ## prefix 15 | 16 | Default prefix: useWP 17 | 18 | Can be changed in config: 19 | 20 | ```ts 21 | wpNuxt: { 22 | ... 23 | composablesPrefix: 'use' 24 | }, 25 | ``` 26 | 27 | # Types 28 | 29 | fragment -> type 30 | -------------------------------------------------------------------------------- /docs/6.under-the-hood/_dir.yml: -------------------------------------------------------------------------------- 1 | title: Under the hood 2 | icon: i-ph-puzzle-piece 3 | -------------------------------------------------------------------------------- /docs/7.troubleshoot/1.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Troubleshooting 3 | description: 4 | --- 5 | 6 | ## 429 Too Many Requests 7 | 8 | When prerendering the pages using "nuxt generate" many requests will be send to the WP GraphQL API in a short period of time.
9 | This can hit the rate limit set by your wordpress hosting provider. 10 | -------------------------------------------------------------------------------- /docs/7.troubleshoot/_dir.yml: -------------------------------------------------------------------------------- 1 | title: Troubleshoot 2 | icon: i-material-symbols-troubleshoot 3 | -------------------------------------------------------------------------------- /docs/index.yml: -------------------------------------------------------------------------------- 1 | navigation: false 2 | title: WPNuxt 3 | description: Use WordPress as a headless CMS with a Nuxt 3 frontend 4 | hero: 5 | badges: 6 | - label: this Nuxt module is still in alpha stage 7 | variant: outline 8 | icon: i-ph-warning 9 | size: xs 10 | trailing: true 11 | to: '/getting-started/installation' 12 | title: 'WPNuxt

Nuxt 3 + headless WordPress' 13 | description: 'Easily fetch content from WordPress using composables and render it with Nuxt 3 components.
Supports Gutenberg blocks, allowing you to provide custom components for each block type.' 14 | button: Get started 15 | sections: 16 | - title: combine the ease of use of WordPress as a CMS
with a cutting edge Nuxt frontend 17 | slot: features 18 | toolsCards: 19 | - title: 'Headless CMS' 20 | description: 'Fetch content from WordPress in your Vue components with powerful composables.' 21 | icon: 'i-ph-files' 22 | to: '' 23 | - title: 'Render Gutenberg blocks as Vue components' 24 | description: 'Create custom components to render specific types of WordPress Gutenberg Blocks' 25 | icon: 'i-ph-funnel' 26 | to: '' 27 | - title: 'Server-side api calls' 28 | description: "WordPress content is fetched on the server using GraphQL api calls and cached on the server" 29 | icon: 'i-ant-design-api-outlined' 30 | to: '' 31 | - title: 'Easy to use
Composables' 32 | description: 'Fetch posts, pages, menus or settings from WordPress with simple composables:
usePosts()
usePages()
useMenu({ name: "main" })
useGeneralSettings()' 33 | class: 'dark bg-gray-900' 34 | align: left 35 | links: 36 | - label: 'Available composables' 37 | icon: i-ph-layout-duotone 38 | to: '/getting-started/composables' 39 | color: black 40 | size: md 41 | - label: 'Source code' 42 | icon: i-ph-app-window-duotone 43 | to: 'https://github.com/wpnuxt/wpnuxt-core/tree/main/src/runtime/composables' 44 | color: gray 45 | size: md 46 | slot: code 47 | code: | 48 | ```vue [posts.vue] 49 | 52 | 53 | 62 | ``` 63 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { createConfigForNuxt } from '@nuxt/eslint-config/flat' 2 | 3 | export default createConfigForNuxt({ 4 | features: { 5 | tooling: true, 6 | stylistic: { 7 | commaDangle: 'never', 8 | braceStyle: '1tbs' 9 | } 10 | }, 11 | dirs: { 12 | src: [ 13 | './playground' 14 | ] 15 | } 16 | }, 17 | { 18 | rules: { 19 | 'vue/multi-word-component-names': 0 20 | }, 21 | ignores: [ 22 | 'wordpress', 23 | 'dist', 24 | 'node_modules', 25 | '.output', 26 | '.nuxt' 27 | ] 28 | }) 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@wpnuxt/core", 3 | "version": "1.0.0-edge.30", 4 | "description": "WPNuxt", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/wpnuxt/wpnuxt-core.git" 8 | }, 9 | "license": "MIT", 10 | "type": "module", 11 | "exports": { 12 | ".": { 13 | "types": "./dist/types.d.mts", 14 | "import": "./dist/module.mjs", 15 | "require": "./dist/module.mjs" 16 | } 17 | }, 18 | "main": "./dist/module.mjs", 19 | "files": [ 20 | "dist" 21 | ], 22 | "publishConfig": { 23 | "access": "public" 24 | }, 25 | "scripts": { 26 | "build": "nuxt-module-build build && pnpm build:web-types", 27 | "build:web-types": "vue-docgen-web-types src/runtime/components/ ./dist/web-types.json", 28 | "prepare": "nuxi prepare .", 29 | "prepack": "nuxt-module-build build", 30 | "generate": "pnpm --filter ./playground/ run generate", 31 | "dev": "pnpm --filter ./playground/ run dev --host app.local", 32 | "dev:build": "pnpm --filter ./playground/ run build", 33 | "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground", 34 | "lint": "eslint .", 35 | "lint:fix": "eslint . --fix", 36 | "lint:docs": "markdownlint ./docs && case-police 'docs/**/*.md' *.md", 37 | "lint:docs:fix": "markdownlint ./docs --fix && case-police 'docs/**/*.md' *.md --fix", 38 | "test": "vitest run", 39 | "test:watch": "vitest watch", 40 | "test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit", 41 | "coverage": "vitest run --coverage", 42 | "typecheck": "vue-tsc --noEmit", 43 | "typecheck:docs": "DOCS_TYPECHECK=true pnpm nuxi prepare && nuxt-content-twoslash verify --content-dir docs", 44 | "release": "pnpm run lint && pnpm run prepack; release-it --git.tagExclude='*[-edge]*'", 45 | "release-edge": "pnpm run lint && pnpm run prepack; release-it --preRelease=edge --config .release-it-edge.json", 46 | "wp:cli": "docker-compose up wordpress-cli", 47 | "wp:stop": "docker-compose down", 48 | "wp:delete": "docker-compose down --volumes --remove-orphans --rmi local && rm -rf ./wordpress/files", 49 | "wp-env": "wp-env", 50 | "wp-env:create": "wp-env start --update && ./wordpress/wp-env-cli.sh", 51 | "start": "pnpm run wp-env:create && pnpm run dev" 52 | }, 53 | "dependencies": { 54 | "@nuxt/kit": "3.17.1", 55 | "@vueuse/nuxt": "^13.1.0", 56 | "defu": "^6.1.4", 57 | "graphql": "^16.11.0", 58 | "nuxt-graphql-middleware": "5.0.0", 59 | "vue-sanitize-directive": "^0.2.1" 60 | }, 61 | "devDependencies": { 62 | "@nuxt/devtools": "^2.4.0", 63 | "@nuxt/eslint-config": "^1.3.0", 64 | "@nuxt/module-builder": "^1.0.1", 65 | "@nuxt/schema": "3.17.1", 66 | "@nuxt/test-utils": "^3.18.0", 67 | "@rollup/rollup-linux-arm64-gnu": "^4.40.1", 68 | "@rollup/rollup-linux-arm64-musl": "^4.40.1", 69 | "@types/node": "22.15.3", 70 | "@vitest/coverage-v8": "^3.1.2", 71 | "@vue/test-utils": "^2.4.6", 72 | "@wordpress/env": "^10.22.0", 73 | "changelogen": "^0.6.1", 74 | "markdownlint-cli": "^0.44.0", 75 | "nuxt": "^3.17.1", 76 | "nuxt-content-twoslash": "^0.1.2", 77 | "release-it": "^19.0.1", 78 | "typescript": "^5.8.3", 79 | "untyped": "2.0.0", 80 | "vite": "^6.3.4", 81 | "vitest": "^3.1.2", 82 | "vue-docgen-web-types": "^0.1.8", 83 | "vue-tsc": "2.2.10" 84 | }, 85 | "peerDependencies": { 86 | "consola": "^3.3.3", 87 | "graphql": "^16.10.0", 88 | "knitwork": "^1.2.0", 89 | "pathe": "^2.0.1", 90 | "scule": "^1.3.0" 91 | }, 92 | "engines": { 93 | "node": ">=20" 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /playground/.env-example: -------------------------------------------------------------------------------- 1 | NUXT_IMAGE_PROVIDER='ipx' 2 | 3 | WPNUXT_WORDPRESS_URL=http://localhost:4000 4 | WPNUXT_FRONTEND_URL=http://localhost:3000 5 | WPNUXT_FAUST_SECRET_KEY=yourKey 6 | 7 | WPNUXT_LOG_LEVEL=4 8 | WPNUXT_ENABLE_CACHE=true 9 | WPNUXT_STAGING=true 10 | -------------------------------------------------------------------------------- /playground/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | -------------------------------------------------------------------------------- /playground/.nuxtrc: -------------------------------------------------------------------------------- 1 | imports.autoImport=true 2 | typescript.includeWorkspace=true 3 | -------------------------------------------------------------------------------- /playground/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { defineAppConfig } from '#imports' 2 | 3 | export default defineAppConfig({ 4 | ui: { 5 | colors: { 6 | primary: 'reefgold', 7 | neutral: 'neutral', 8 | gray: 'neutral' 9 | } 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /playground/app/app.vue: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /playground/app/assets/css/main.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | @import "@nuxt/ui-pro"; 3 | 4 | @theme { 5 | --font-sans: DM Sans, sans-serif; 6 | 7 | --color-reefgold-50: #faf9f0; 8 | --color-reefgold-100: #f5f3e1; 9 | --color-reefgold-200: #e6e2b5; 10 | --color-reefgold-300: #d6d090; 11 | --color-reefgold-400: #baaf4e; 12 | --color-reefgold-500: #9c8e1b; 13 | --color-reefgold-600: #8c7815; 14 | --color-reefgold-700: #755d0f; 15 | --color-reefgold-800: #5e4509; 16 | --color-reefgold-900: #452e06; 17 | --color-reefgold-950: #2e1b02; 18 | 19 | --color-woodsmoke-50: #64646C; 20 | --color-woodsmoke-100: #5A5A62; 21 | --color-woodsmoke-200: #47474D; 22 | --color-woodsmoke-300: #333337; 23 | --color-woodsmoke-400: #202022; 24 | --color-woodsmoke-500: #0C0C0D; 25 | --color-woodsmoke-600: #000000; 26 | --color-woodsmoke-700: #000000; 27 | --color-woodsmoke-800: #000000; 28 | --color-woodsmoke-900: #000000; 29 | --color-woodsmoke-950: #000000; 30 | } 31 | -------------------------------------------------------------------------------- /playground/app/components/HeaderComponent.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 68 | -------------------------------------------------------------------------------- /playground/app/components/LatestPost.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 29 | -------------------------------------------------------------------------------- /playground/app/components/PagePlaceholder.vue: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /playground/app/components/PostAside.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 51 | -------------------------------------------------------------------------------- /playground/app/components/PostPlaceholder.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /playground/app/components/PrevNext.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 42 | -------------------------------------------------------------------------------- /playground/app/components/UsePosts.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 42 | -------------------------------------------------------------------------------- /playground/app/components/UsePostsByCategory.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 31 | -------------------------------------------------------------------------------- /playground/app/components/blocks/CoreButton.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | -------------------------------------------------------------------------------- /playground/app/error.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 36 | -------------------------------------------------------------------------------- /playground/app/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /playground/app/pages/[...slug].vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 37 | -------------------------------------------------------------------------------- /playground/app/pages/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /playground/app/pages/test/composables.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 58 | -------------------------------------------------------------------------------- /playground/app/pages/test/config.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 25 | -------------------------------------------------------------------------------- /playground/app/pages/test/errorHandling.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /playground/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import withNuxt from './.nuxt/eslint.config.mjs' 2 | 3 | export default withNuxt( 4 | { 5 | files: ['**/pages/*.vue'], 6 | rules: { 7 | 'vue/multi-word-component-names': 'off' 8 | } 9 | } 10 | ) 11 | -------------------------------------------------------------------------------- /playground/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNuxtConfig({ 2 | modules: [ 3 | '@nuxt/eslint', 4 | '@nuxt/image', 5 | '@nuxt/ui-pro', 6 | '@nuxtjs/mdc', 7 | '../src/module' 8 | ], 9 | 10 | devtools: { enabled: true }, 11 | 12 | css: ['~/assets/css/main.css'], 13 | 14 | ui: { 15 | colorMode: true 16 | }, 17 | 18 | routeRules: { 19 | '/wp-content/**': { proxy: { to: 'http://localhost:4000/wp-content/**' } } 20 | }, 21 | 22 | future: { 23 | compatibilityVersion: 4 24 | }, 25 | compatibilityDate: '2025-01-09', 26 | 27 | eslint: { 28 | config: { 29 | stylistic: { 30 | commaDangle: 'never', 31 | braceStyle: '1tbs' 32 | } 33 | }, 34 | checker: { 35 | lintOnStart: true, 36 | fix: true 37 | } 38 | }, 39 | 40 | wpNuxt: { 41 | wordpressUrl: 'https://wordpress.wpnuxt.com', 42 | frontendUrl: 'https://demo.wpnuxt.com', 43 | enableCache: true, 44 | staging: false, 45 | logLevel: 4, 46 | downloadSchema: true, 47 | composablesPrefix: 'use' 48 | } 49 | }) 50 | -------------------------------------------------------------------------------- /playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "wpnuxt-core-playground", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "nuxi dev", 7 | "build": "nuxi build", 8 | "generate": "nuxi generate", 9 | "lint": "eslint .", 10 | "lint:fix": "eslint . --fix" 11 | }, 12 | "devDependencies": { 13 | "@iconify-json/heroicons": "^1.2.2", 14 | "@iconify-json/lucide": "^1.2.39", 15 | "@iconify-json/mdi": "^1.2.2", 16 | "@iconify-json/svg-spinners": "^1.2.2", 17 | "@iconify-json/uil": "^1.2.3", 18 | "@nuxt/eslint": "^1.3.0", 19 | "@nuxt/image": "1.10.0", 20 | "nuxt": "^3.17.1", 21 | "vite-plugin-eslint2": "^5.0.3" 22 | }, 23 | "dependencies": { 24 | "@nuxt/ui-pro": "3.1.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /playground/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nuxt/tsconfig.json", 3 | "exclude": [ 4 | "node_modules", 5 | "dist", 6 | ".nuxt", 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - '.' 3 | - 'playground' 4 | -------------------------------------------------------------------------------- /src/context.ts: -------------------------------------------------------------------------------- 1 | import { promises as fsp } from 'node:fs' 2 | import { upperFirst } from 'scule' 3 | import type { Import } from 'unimport' 4 | import type { WPNuxtQuery } from './types' 5 | import { getLogger } from './utils' 6 | import { parseDoc } from './useParser' 7 | 8 | export interface WPNuxtContext { 9 | composablesPrefix: string 10 | template?: string 11 | fns: WPNuxtQuery[] 12 | fnImports?: Import[] 13 | generateImports?: () => string 14 | generateDeclarations?: () => string 15 | docs?: string[] 16 | } 17 | 18 | export async function prepareContext(ctx: WPNuxtContext) { 19 | const logger = getLogger() 20 | if (ctx.docs) { 21 | await prepareFunctions(ctx) 22 | } 23 | 24 | const fnName = (fn: string) => ctx.composablesPrefix + upperFirst(fn) 25 | const fnExp = (q: WPNuxtQuery, typed = false) => { 26 | const functionName = fnName(q.name) 27 | if (!typed) { 28 | return `export const ${functionName} = (params) => useWPContent('${q.operation}', '${q.name}', [${q.nodes?.map(n => `'${n}'`).join(',')}], false, params)` 29 | } 30 | const fragmentSuffix = q.fragments?.length && q.nodes?.includes('nodes') ? '[]' : '' 31 | const fragments = q.fragments?.length ? q.fragments.map(f => `${f}Fragment${fragmentSuffix}`).join(' | ') : 'any' 32 | return ` export const ${functionName}: (params?: ${q.name}QueryVariables) => AsyncData<${fragments}, FetchError | null | undefined>` 33 | } 34 | 35 | ctx.generateImports = () => [ 36 | ...ctx.fns.map(f => fnExp(f)) 37 | ].join('\n') 38 | 39 | const types: string[] = [] 40 | ctx.fns.forEach(o => types.push(...getQueryTypeTemplate(o))) 41 | ctx.generateDeclarations = () => [ 42 | `import type { ${[...new Set(types)].join(', ')} } from '#build/graphql-operations'`, 43 | 'import { AsyncData } from \'nuxt/app\'', 44 | 'import { FetchError } from \'ofetch\'', 45 | 'declare module \'#wpnuxt\' {', 46 | ...([ 47 | ...ctx.fns!.map(f => fnExp(f, true)) 48 | ]), 49 | '}' 50 | ].join('\n') 51 | 52 | ctx.fnImports = ctx.fns.map((fn): Import => ({ from: '#wpnuxt', name: fnName(fn.name) })) 53 | 54 | logger.debug('generated WPNuxt composables: ') 55 | ctx.fns.forEach(f => logger.debug(` ${fnName(f.name)}()`)) 56 | } 57 | 58 | function getQueryTypeTemplate(q: WPNuxtQuery): string[] { 59 | const types: string[] = [] 60 | types.push(`${q.name}QueryVariables`) 61 | if (q.fragments && q.fragments.length > 0) { 62 | q.fragments.forEach(f => types.push(`${f}Fragment`)) 63 | } 64 | return types 65 | } 66 | 67 | async function prepareFunctions(ctx: WPNuxtContext) { 68 | if (!ctx.docs) { 69 | getLogger().error('no GraphQL query documents were found!') 70 | return 71 | } 72 | for await (const doc of ctx.docs) { 73 | const operations = await parseDoc(await fsp.readFile(doc, 'utf8')) 74 | operations.forEach((query) => { 75 | ctx.fns.push(query) 76 | }) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/generate.ts: -------------------------------------------------------------------------------- 1 | import { statSync } from 'node:fs' 2 | import { type Resolver, resolveFiles } from '@nuxt/kit' 3 | import { prepareContext, type WPNuxtContext } from './context' 4 | 5 | const allowDocument = (f: string, resolver: Resolver) => { 6 | const isSchema = f.match(/([^/]+)\.(gql|graphql)$/)?.[0]?.toLowerCase().includes('schema') 7 | return !isSchema && !!statSync(resolver.resolve(f)).size 8 | } 9 | export async function generateWPNuxtComposables(ctx: WPNuxtContext, queryOutputPath: string, resolver: Resolver) { 10 | const gqlMatch = '**/*.{gql,graphql}' 11 | const documents: string[] = [] 12 | const files = (await resolveFiles(queryOutputPath, [gqlMatch, '!**/schemas'], { followSymbolicLinks: false })).filter(doc => allowDocument(doc, resolver)) 13 | documents.push(...files) 14 | ctx.docs = documents 15 | 16 | await prepareContext(ctx) 17 | } 18 | -------------------------------------------------------------------------------- /src/module.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import { defineNuxtModule, addComponentsDir, addServerHandler, createResolver, installModule, addTemplate, addTypeTemplate, addImports, type Resolver, addPlugin, hasNuxtModule } from '@nuxt/kit' 3 | import { consola } from 'consola' 4 | import { name, version } from '../package.json' 5 | import type { WPNuxtConfig } from './types' 6 | import { initLogger, mergeQueries, validateConfig } from './utils' 7 | import { generateWPNuxtComposables } from './generate' 8 | import type { WPNuxtContext } from './context' 9 | 10 | const defaultConfigs: WPNuxtConfig = { 11 | wordpressUrl: '', 12 | frontendUrl: '', 13 | defaultMenuName: 'main', 14 | enableCache: true, 15 | staging: false, 16 | logLevel: 3, 17 | composablesPrefix: 'useWP', 18 | hasBlocksModule: false, 19 | hasAuthModule: false 20 | } 21 | 22 | export default defineNuxtModule({ 23 | meta: { 24 | name, 25 | version, 26 | configKey: 'wpNuxt', 27 | nuxt: '>=3.1.0' 28 | }, 29 | // Default configuration options of the Nuxt module 30 | defaults: defaultConfigs, 31 | async setup(options: any, nuxt: any) { 32 | const startTime = new Date().getTime() 33 | consola.log('::: Starting WPNuxt setup ::: ') 34 | 35 | const publicWPNuxtConfig: WPNuxtConfig = { 36 | wordpressUrl: process.env.WPNUXT_WORDPRESS_URL || options.wordpressUrl!, 37 | frontendUrl: process.env.WPNUXT_FRONTEND_URL || options.frontendUrl!, 38 | defaultMenuName: process.env.WPNUXT_DEFAULT_MENU_NAME || options.defaultMenuName!, 39 | enableCache: process.env.WPNUXT_ENABLE_CACHE ? process.env.WPNUXT_ENABLE_CACHE === 'true' : options.enableCache!, 40 | staging: process.env.WPNUXT_STAGING === 'true' || options.staging!, 41 | downloadSchema: process.env.WPNUXT_DOWNLOAD_SCHEMA === 'true' || options.downloadSchema, 42 | logLevel: process.env.WPNUXT_LOG_LEVEL ? Number.parseInt(process.env.WPNUXT_LOG_LEVEL) : options.logLevel, 43 | composablesPrefix: process.env.WPNUXT_COMPOSABLES_PREFIX || options.composablesPrefix, 44 | hasBlocksModule: hasNuxtModule('@wpnuxt/blocks'), 45 | hasAuthModule: hasNuxtModule('@wpnuxt/auth') 46 | } 47 | nuxt.options.runtimeConfig.public.wpNuxt = publicWPNuxtConfig 48 | validateConfig(publicWPNuxtConfig) 49 | const logger = initLogger(publicWPNuxtConfig.logLevel) 50 | logger.info('config:', publicWPNuxtConfig) 51 | 52 | logger.info('Connecting GraphQL to', publicWPNuxtConfig.wordpressUrl) 53 | logger.info('frontendUrl:', publicWPNuxtConfig.frontendUrl) 54 | if (publicWPNuxtConfig.enableCache) logger.info('Cache enabled') 55 | logger.debug('Debug mode enabled, log level:', publicWPNuxtConfig.logLevel) 56 | if (publicWPNuxtConfig.staging) logger.info('Staging enabled') 57 | 58 | const { resolve } = createResolver(import.meta.url) 59 | const resolveRuntimeModule = (path: string) => resolve('./runtime', path) 60 | const srcResolver: Resolver = createResolver(nuxt.options.srcDir) 61 | 62 | nuxt.options.alias['#wpnuxt'] = resolve(nuxt.options.buildDir, 'wpnuxt') 63 | nuxt.options.alias['#wpnuxt/*'] = resolve(nuxt.options.buildDir, 'wpnuxt', '*') 64 | nuxt.options.alias['#wpnuxt/types'] = resolve('./types') 65 | nuxt.options.nitro.alias = nuxt.options.nitro.alias || {} 66 | nuxt.options.nitro.alias['#wpnuxt/types'] = resolve('./types') 67 | 68 | nuxt.options.nitro.externals = nuxt.options.nitro.externals || {} 69 | nuxt.options.nitro.externals.inline = nuxt.options.nitro.externals.inline || [] 70 | 71 | addPlugin({ 72 | src: resolveRuntimeModule('plugins/vue-sanitize-directive') 73 | }) 74 | 75 | addImports([ 76 | { name: 'isStaging', as: 'isStaging', from: resolveRuntimeModule('./composables/isStaging') }, 77 | { name: 'useWPContent', as: 'useWPContent', from: resolveRuntimeModule('./composables/useWPContent') }, 78 | { name: 'parseDoc', as: 'parseDoc', from: resolveRuntimeModule('./composables/useParser') }, 79 | { name: 'usePrevNextPost', as: 'usePrevNextPost', from: resolveRuntimeModule('./composables/usePrevNextPost') }, 80 | 81 | { name: 'loginUser', as: 'loginUser', from: resolveRuntimeModule('./composables/user') }, 82 | { name: 'logoutUser', as: 'logoutUser', from: resolveRuntimeModule('./composables/user') }, 83 | { name: 'getCurrentUserId', as: 'getCurrentUserId', from: resolveRuntimeModule('./composables/user') }, 84 | { name: 'getCurrentUserName', as: 'getCurrentUserName', from: resolveRuntimeModule('./composables/user') }, 85 | 86 | { name: 'useTokens', as: 'useTokens', from: resolveRuntimeModule('./composables/useTokens') }, 87 | { name: 'useWPUri', as: 'useWPUri', from: resolveRuntimeModule('./composables/useWPUri') }, 88 | { name: 'useFeaturedImage', as: 'useFeaturedImage', from: resolveRuntimeModule('./composables/useFeaturedImage') } 89 | ]) 90 | 91 | addComponentsDir({ 92 | path: resolveRuntimeModule('./components'), 93 | pathPrefix: false, 94 | prefix: '', 95 | global: true 96 | }) 97 | addServerHandler({ 98 | route: '/api/wpContent', 99 | handler: resolveRuntimeModule('./server/api/wpContent.post') 100 | }) 101 | addServerHandler({ 102 | route: '/api/config', 103 | handler: resolveRuntimeModule('./server/api/config') 104 | }) 105 | 106 | await installModule('@vueuse/nuxt', {}) 107 | 108 | const mergedQueriesFolder = await mergeQueries(nuxt) 109 | 110 | await installModule('nuxt-graphql-middleware', { 111 | debug: publicWPNuxtConfig.logLevel ? publicWPNuxtConfig.logLevel > 3 : false, 112 | graphqlEndpoint: `${publicWPNuxtConfig.wordpressUrl}/graphql`, 113 | downloadSchema: publicWPNuxtConfig.downloadSchema, 114 | codegenConfig: { 115 | debugMode: publicWPNuxtConfig.logLevel ? publicWPNuxtConfig.logLevel > 3 : false, 116 | useCache: false 117 | }, 118 | codegenSchemaConfig: { 119 | urlSchemaOptions: { 120 | headers: { 121 | Authorization: 'server-token' 122 | } 123 | } 124 | }, 125 | outputDocuments: true, 126 | autoImportPatterns: [mergedQueriesFolder], 127 | includeComposables: true, 128 | devtools: true 129 | }) 130 | 131 | logger.trace('Start generating composables') 132 | 133 | const ctx: WPNuxtContext = await { 134 | fns: [], 135 | fnImports: [], 136 | composablesPrefix: publicWPNuxtConfig.composablesPrefix 137 | } 138 | await generateWPNuxtComposables(ctx, mergedQueriesFolder, srcResolver) 139 | 140 | addTemplate({ 141 | write: true, 142 | filename: 'wpnuxt/index.mjs', 143 | getContents: () => ctx.generateImports?.() || '' 144 | }) 145 | addTypeTemplate({ 146 | write: true, 147 | filename: 'wpnuxt/index.d.ts', 148 | getContents: () => ctx.generateDeclarations?.() || '' 149 | }) 150 | nuxt.hook('imports:extend', (autoimports: any) => { 151 | autoimports.push(...(ctx.fnImports || [])) 152 | }) 153 | 154 | nuxt.hook('nitro:init', async (nitro: any) => { 155 | // Remove content cache when nitro starts 156 | const keys = await nitro.storage.getKeys('cache:api:wpContent') 157 | keys.forEach(async (key: any) => { 158 | if (key.startsWith('cache:api:wpContent')) await nitro.storage.removeItem(key) 159 | }) 160 | }) 161 | 162 | const endTime = new Date().getTime() 163 | logger.success('::: Finished WPNuxt setup in ' + (endTime - startTime) + 'ms ::: ') 164 | } 165 | }) 166 | 167 | declare module '@nuxt/schema' { 168 | 169 | interface RuntimeConfig { 170 | wpNuxt: { 171 | faustSecretKey: string 172 | } 173 | } 174 | 175 | interface PublicRuntimeConfig { 176 | wpNuxt: WPNuxtConfig 177 | } 178 | 179 | interface ConfigSchema { 180 | wpNuxt: { 181 | faustSecretKey: string 182 | } 183 | runtimeConfig: { 184 | public?: { 185 | wpNuxt: WPNuxtConfig 186 | } 187 | } 188 | } 189 | } 190 | export type { WPNuxtConfig, WPNuxtConfigQueries, WPNuxtQuery } from './types' 191 | -------------------------------------------------------------------------------- /src/runtime/components/ContentRenderer.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /src/runtime/components/StagingBanner.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 105 | 106 | 204 | -------------------------------------------------------------------------------- /src/runtime/components/WPContent.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 27 | -------------------------------------------------------------------------------- /src/runtime/components/WPNuxtLogo.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | 36 | -------------------------------------------------------------------------------- /src/runtime/components/WordPressLogo.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /src/runtime/composables/index.ts: -------------------------------------------------------------------------------- 1 | export { isStaging } from './isStaging' 2 | export { useWPContent } from './useWPContent' 3 | export { useWPUri } from './useWPUri' 4 | -------------------------------------------------------------------------------- /src/runtime/composables/isStaging.ts: -------------------------------------------------------------------------------- 1 | import { useRuntimeConfig } from '#imports' 2 | 3 | const _isStaging = async () => { 4 | const config = useRuntimeConfig() 5 | return config.public.wpNuxt.staging 6 | } 7 | export const isStaging = _isStaging 8 | -------------------------------------------------------------------------------- /src/runtime/composables/useFeaturedImage.ts: -------------------------------------------------------------------------------- 1 | import { getRelativeImagePath } from '../util/images' 2 | import type { NodeWithFeaturedImage } from '#graphql-operations' 3 | 4 | const _useFeaturedImage = (contentNode: NodeWithFeaturedImage): string | undefined => { 5 | const sourceUrl = contentNode?.featuredImage?.node?.sourceUrl 6 | if (sourceUrl) return getRelativeImagePath(sourceUrl) 7 | else return undefined 8 | } 9 | 10 | export const useFeaturedImage = _useFeaturedImage 11 | -------------------------------------------------------------------------------- /src/runtime/composables/usePrevNextPost.ts: -------------------------------------------------------------------------------- 1 | import { OperationTypeNode } from 'graphql' 2 | import { useWPContent } from './useWPContent' 3 | 4 | const _usePrevNextPost = async (currentPostPath: string) => { 5 | const currentPostSlug = currentPostPath.replaceAll('/', '') 6 | const allPosts = await getAllPosts() 7 | if (!allPosts) return { prev: null, next: null } 8 | const currentIndex: number = allPosts.slugs.findIndex((slug: string) => slug === currentPostSlug) 9 | const nextPost = currentIndex > 0 ? allPosts.slugs[currentIndex - 1] : null 10 | const prevPost = allPosts.slugs.length > (currentIndex + 1) ? allPosts.slugs[currentIndex + 1] : null 11 | 12 | return { 13 | prev: prevPost ? prevPost : null, 14 | next: nextPost ? nextPost : null 15 | } 16 | } 17 | 18 | const getAllPosts = async () => { 19 | const { data: allPosts } = await useWPContent(OperationTypeNode.QUERY, 'Posts', ['posts', 'nodes'], false) 20 | if (allPosts) { 21 | return { 22 | slugs: allPosts?.map((post) => { 23 | if (post) return post.slug 24 | else return null 25 | }) 26 | } 27 | } 28 | return 29 | } 30 | 31 | export const usePrevNextPost = _usePrevNextPost 32 | -------------------------------------------------------------------------------- /src/runtime/composables/useWPContent.ts: -------------------------------------------------------------------------------- 1 | import type { FetchError } from 'ofetch' 2 | import type { OperationTypeNode } from 'graphql' 3 | import { getRelativeImagePath } from '../util/images' 4 | import type { AsyncData } from '#app' 5 | 6 | const _useWPContent = async (operation: OperationTypeNode, queryName: string, nodes: string[], fixImagePaths: boolean, params?: T) => { 7 | const { data, error } = await $fetch>('/api/wpContent', { 8 | method: 'POST', 9 | body: { 10 | operation, 11 | queryName, 12 | params 13 | } 14 | }) 15 | return { 16 | data: data ? transformData(data, nodes, fixImagePaths) : undefined, 17 | error 18 | } 19 | } 20 | 21 | const transformData = (data: unknown, nodes: string[], fixImagePaths: boolean): T => { 22 | const transformedData = findData(data, nodes) 23 | if (fixImagePaths && transformedData?.featuredImage?.node?.sourceUrl) { 24 | transformedData.featuredImage.node.relativePath 25 | = getRelativeImagePath(transformedData.featuredImage.node.sourceUrl) 26 | } 27 | return transformedData as T 28 | } 29 | 30 | const findData = (data: unknown, nodes: string[]) => { 31 | if (nodes.length === 0) return data 32 | if (nodes.length > 0) { 33 | return nodes.reduce((acc, node) => { 34 | return acc[node] ?? acc 35 | }, data) 36 | } 37 | } 38 | 39 | export const useWPContent = _useWPContent 40 | -------------------------------------------------------------------------------- /src/runtime/composables/useWPUri.ts: -------------------------------------------------------------------------------- 1 | import { useRuntimeConfig } from '#imports' 2 | 3 | const _useWPUri = () => { 4 | const config = useRuntimeConfig() 5 | const base = config.public.wpNuxt.wordpressUrl 6 | const admin = base + '/wp-admin' 7 | const pagesAdmin = base + '/wp-admin/edit.php?post_type=page' 8 | const postAdmin = base + '/wp-admin/edit.php?post_type=post' 9 | const settingsEdit = base + '/wp-admin/options-general.php' 10 | const postEdit = (path: string) => { 11 | if (path) 12 | return base + '/wp-admin/post.php?post=' + path + '&action=edit' 13 | else return postAdmin 14 | } 15 | 16 | return { 17 | base, 18 | admin, 19 | pagesAdmin, 20 | postAdmin, 21 | postEdit, 22 | settingsEdit 23 | } 24 | } 25 | 26 | export const useWPUri = _useWPUri 27 | -------------------------------------------------------------------------------- /src/runtime/plugins/vue-sanitize-directive.ts: -------------------------------------------------------------------------------- 1 | import { defineNuxtPlugin } from 'nuxt/app' 2 | import VueSanitize from 'vue-sanitize-directive' 3 | 4 | export default defineNuxtPlugin((nuxtApp) => { 5 | nuxtApp.vueApp.use(VueSanitize) 6 | }) 7 | -------------------------------------------------------------------------------- /src/runtime/queries/GeneralSettings.gql: -------------------------------------------------------------------------------- 1 | query GeneralSettings { 2 | generalSettings { 3 | ...GeneralSettings 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/runtime/queries/Menu.gql: -------------------------------------------------------------------------------- 1 | query Menu($name: ID! = "main", $idType: MenuNodeIdTypeEnum! = NAME) { 2 | menu(id: $name, idType: $idType) { 3 | menuItems { 4 | nodes { 5 | ...MenuItem 6 | } 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/runtime/queries/Node.gql: -------------------------------------------------------------------------------- 1 | query NodeByUri($uri: String!) { 2 | nodeByUri(uri: $uri) { 3 | ...Page 4 | ...Post 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/runtime/queries/Pages.gql: -------------------------------------------------------------------------------- 1 | query Pages($limit: Int = 10) { 2 | pages(first: $limit) { 3 | nodes { 4 | ...Page 5 | } 6 | } 7 | } 8 | query PageByUri($uri: String!) { 9 | nodeByUri(uri: $uri) { 10 | ...Page 11 | } 12 | } 13 | query PageById($id: ID!) { 14 | page(id: $id, idType: DATABASE_ID, asPreview: true) { 15 | ...Page 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/runtime/queries/Posts.gql: -------------------------------------------------------------------------------- 1 | query Posts($limit: Int = 10) { 2 | posts(first: $limit) { 3 | nodes { 4 | ...Post 5 | } 6 | } 7 | } 8 | query PostByUri($uri: String!) { 9 | nodeByUri(uri: $uri) { 10 | ...Post 11 | } 12 | } 13 | query PostById($id: ID!, $asPreview: Boolean = false) { 14 | post(id: $id, idType: DATABASE_ID, asPreview: $asPreview) { 15 | ...Post 16 | } 17 | } 18 | 19 | query PostsByCategoryName($categoryName: String!, $limit: Int = 10) { 20 | posts(first: $limit, where: {categoryName: $categoryName}) { 21 | nodes { 22 | ...Post 23 | } 24 | } 25 | } 26 | query PostsByCategoryId($categoryId: Int!, $limit: Int = 10) { 27 | posts(first: $limit, where: {categoryId: $categoryId}) { 28 | nodes { 29 | ...Post 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/runtime/queries/Revisions.gql: -------------------------------------------------------------------------------- 1 | query Revisions { 2 | revisions { 3 | nodes { 4 | date 5 | uri 6 | isPreview 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/runtime/queries/Viewer.gql: -------------------------------------------------------------------------------- 1 | query Viewer { 2 | viewer { 3 | username 4 | userId 5 | id 6 | email 7 | description 8 | firstName 9 | lastName 10 | locale 11 | url 12 | uri 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/runtime/queries/fragments/ContentNode.fragment.gql: -------------------------------------------------------------------------------- 1 | fragment ContentNode on ContentNode { 2 | # contentType 3 | contentTypeName 4 | databaseId 5 | date 6 | dateGmt 7 | desiredSlug 8 | # editingLockedBy 9 | enclosure 10 | # enqueuedScripts 11 | # enqueuedStylesheets 12 | guid 13 | id 14 | isContentNode 15 | isPreview 16 | isRestricted 17 | isTermNode 18 | # lastEditedBy 19 | link 20 | modified 21 | modifiedGmt 22 | previewRevisionDatabaseId 23 | previewRevisionId 24 | slug 25 | status 26 | # template 27 | uri 28 | } 29 | -------------------------------------------------------------------------------- /src/runtime/queries/fragments/GeneralSettings.fragment.gql: -------------------------------------------------------------------------------- 1 | fragment GeneralSettings on GeneralSettings { 2 | title 3 | description 4 | url 5 | dateFormat 6 | language 7 | startOfWeek 8 | timezone 9 | timeFormat 10 | } 11 | -------------------------------------------------------------------------------- /src/runtime/queries/fragments/MediaItem.fragment.gql: -------------------------------------------------------------------------------- 1 | fragment MediaItem on MediaItem { 2 | altText 3 | # ancestors 4 | # author 5 | authorDatabaseId 6 | authorId 7 | caption 8 | # children 9 | commentCount 10 | commentStatus 11 | # comments 12 | # contentType 13 | contentTypeName 14 | databaseId 15 | date 16 | dateGmt 17 | description 18 | desiredSlug 19 | # editingLockedBy 20 | enclosure 21 | # enqueuedScripts 22 | # enqueuedStylesheets 23 | fileSize 24 | guid 25 | id 26 | isContentNode 27 | isPreview 28 | isRestricted 29 | isTermNode 30 | # lastEditedBy 31 | link 32 | # mediaDetails 33 | mediaItemId 34 | mediaItemUrl 35 | mediaType 36 | mimeType 37 | modified 38 | modifiedGmt 39 | # parent 40 | parentDatabaseId 41 | parentId 42 | previewRevisionDatabaseId 43 | previewRevisionId 44 | sizes 45 | slug 46 | sourceUrl 47 | srcSet 48 | status 49 | # template 50 | title 51 | uri 52 | } 53 | -------------------------------------------------------------------------------- /src/runtime/queries/fragments/MenuItem.fragment.gql: -------------------------------------------------------------------------------- 1 | fragment MenuItem on MenuItem { 2 | label 3 | uri 4 | } 5 | -------------------------------------------------------------------------------- /src/runtime/queries/fragments/NodeWithContentEditor.fragment.gql: -------------------------------------------------------------------------------- 1 | fragment NodeWithContentEditor on NodeWithContentEditor { 2 | content 3 | } 4 | -------------------------------------------------------------------------------- /src/runtime/queries/fragments/NodeWithExcerpt.fragment.gql: -------------------------------------------------------------------------------- 1 | fragment NodeWithExcerpt on NodeWithExcerpt { 2 | excerpt 3 | } 4 | -------------------------------------------------------------------------------- /src/runtime/queries/fragments/NodeWithFeaturedImage.fragment.gql: -------------------------------------------------------------------------------- 1 | fragment NodeWithFeaturedImage on NodeWithFeaturedImage { 2 | featuredImage { 3 | ...NodeWithFeaturedImageToMediaItemConnectionEdge 4 | } 5 | featuredImageDatabaseId 6 | featuredImageId 7 | id 8 | } 9 | -------------------------------------------------------------------------------- /src/runtime/queries/fragments/NodeWithFeaturedImageToMediaItemConnectionEdge.fragment.gql: -------------------------------------------------------------------------------- 1 | fragment NodeWithFeaturedImageToMediaItemConnectionEdge on NodeWithFeaturedImageToMediaItemConnectionEdge { 2 | cursor 3 | node { 4 | ...MediaItem 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/runtime/queries/fragments/Page.fragment.gql: -------------------------------------------------------------------------------- 1 | fragment Page on Page { 2 | ...ContentNode 3 | ...NodeWithFeaturedImage 4 | ...NodeWithContentEditor 5 | isFrontPage 6 | isPostsPage 7 | isPreview 8 | isPrivacyPage 9 | isRestricted 10 | isRevision 11 | title 12 | } 13 | -------------------------------------------------------------------------------- /src/runtime/queries/fragments/Post.fragment.gql: -------------------------------------------------------------------------------- 1 | fragment Post on Post { 2 | ...NodeWithExcerpt 3 | ...ContentNode 4 | ...NodeWithFeaturedImage 5 | ...NodeWithContentEditor 6 | title 7 | categories { 8 | nodes { 9 | id 10 | name 11 | uri 12 | description 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/runtime/server/api/config.ts: -------------------------------------------------------------------------------- 1 | import { defineCachedEventHandler, useRuntimeConfig } from '#imports' 2 | 3 | export default defineCachedEventHandler(async (event) => { 4 | const config = useRuntimeConfig(event) 5 | return { 6 | wpNuxt: config.public.wpNuxt 7 | } 8 | }) 9 | -------------------------------------------------------------------------------- /src/runtime/server/api/wpContent.post.ts: -------------------------------------------------------------------------------- 1 | import type { H3Event } from 'h3' 2 | import { readBody } from 'h3' 3 | import { defineCachedEventHandler } from '#imports' 4 | import type { GraphqlResponse } from '#graphql-documents' 5 | 6 | export default defineCachedEventHandler(async (event: H3Event) => { 7 | const body = await readBody(event) 8 | 9 | if (!body || !body.queryName) { 10 | throw new Error( 11 | 'The request must contain a queryName' 12 | ) 13 | } 14 | return $fetch('/api/graphql_middleware/' + (body.operation || 'query') + '/' + body.queryName, { 15 | method: body.operation === 'mutation' ? 'POST' : 'GET', 16 | params: body.operation === 'query' ? buildRequestParams(body.params) : undefined, 17 | body: body.operation === 'mutation' ? body.params : undefined, 18 | headers: { 19 | Authorization: `Bearer ${event.context.accessToken}` 20 | } 21 | }).then((v: GraphqlResponse) => { 22 | return { 23 | data: v.data, 24 | error: v.errors || undefined 25 | } 26 | }) 27 | }, { 28 | group: 'api', 29 | name: 'wpContent', 30 | getKey: async (event: H3Event) => { 31 | const body = await readBody(event) 32 | return `${body.queryName}${body.params ? '_' + JSON.stringify(body.params).replaceAll('"', '').replaceAll(':', '_') : ''}` 33 | }, 34 | swr: true, 35 | maxAge: 60 * 5 // 5 minutes 36 | }) 37 | 38 | /** 39 | * Get the parameters for the GraphQL middleware query. 40 | */ 41 | export function buildRequestParams( 42 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 43 | variables?: Record | undefined | null 44 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 45 | ): Record { 46 | if (!variables) { 47 | return {} 48 | } 49 | // Determine if each variable can safely be passed as query parameter. 50 | // This is only the case for strings. 51 | for (const key in variables) { 52 | if (typeof variables[key] !== 'string') { 53 | return { 54 | __variables: JSON.stringify(variables) 55 | } 56 | } 57 | } 58 | 59 | return variables 60 | } 61 | -------------------------------------------------------------------------------- /src/runtime/server/index.ts: -------------------------------------------------------------------------------- 1 | export function usePosts() { 2 | return $fetch('/api/wpContent', { 3 | method: 'POST', 4 | body: { 5 | queryName: 'Posts' 6 | } 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /src/runtime/util/images.ts: -------------------------------------------------------------------------------- 1 | const getRelativeImagePath = function getRelativeImagePath(imgUrl: string): string { 2 | if (imgUrl) { 3 | const url = new URL(imgUrl) 4 | imgUrl = url.pathname 5 | } 6 | return imgUrl 7 | } 8 | 9 | export { getRelativeImagePath } 10 | -------------------------------------------------------------------------------- /src/runtime/util/logger.ts: -------------------------------------------------------------------------------- 1 | import { createConsola, type LogLevel } from 'consola' 2 | import { ref } from 'vue' 3 | import { useRuntimeConfig } from '#imports' 4 | 5 | const loggerRef = ref() 6 | 7 | export const useLogger = () => { 8 | const logger = loggerRef.value 9 | if (logger) { 10 | return logger 11 | } else { 12 | const config = useRuntimeConfig() 13 | return initLogger(config.public.wpNuxt.logLevel ? config.public.wpNuxt.logLevel : 3) 14 | } 15 | } 16 | 17 | export const initLogger = (logLevel: LogLevel | undefined) => { 18 | loggerRef.value = createConsola({ 19 | level: logLevel ? logLevel : 3, 20 | formatOptions: { 21 | colors: true, 22 | compact: true, 23 | date: true, 24 | fancy: true 25 | } 26 | }).withTag('wpnuxt') 27 | return loggerRef.value 28 | } 29 | -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | export interface WPNuxtConfig { 2 | 3 | /** 4 | * Log level for the WPNuxt module 5 | * @default 3 6 | * @example 0 = silent, 1 = error, 2 = warn, 3 = info, 4 = debug, 5 = trace 7 | */ 8 | logLevel?: number 9 | /** 10 | * URL of the WordPress site 11 | * 12 | * There is no default value for this option, so it's required. 13 | * 14 | * @example 'https://wordpress.wpnuxt.com' 15 | */ 16 | wordpressUrl: string 17 | frontendUrl: string 18 | 19 | defaultMenuName?: string 20 | 21 | enableCache?: boolean 22 | 23 | staging?: boolean 24 | 25 | /** 26 | * @default 'useWP' 27 | */ 28 | composablesPrefix: string 29 | 30 | queries?: WPNuxtConfigQueries 31 | 32 | /** 33 | * Download the GraphQL schema and store it on disk. 34 | * 35 | * When setting this to false, the module will expect a graphql.schema file to be available. 36 | * You could first enable this, commit the schema file and then set downloadSchema to false to avoid have to query the graphql introspection on each start of the application 37 | * 38 | * @default true 39 | */ 40 | downloadSchema?: boolean 41 | 42 | hasBlocksModule?: boolean 43 | hasAuthModule?: boolean 44 | } 45 | 46 | export interface WPNuxtConfigQueries { 47 | 48 | /** 49 | * Folder for user defined queries 50 | * 51 | * relative to the src dir of your nuxt app 52 | * 53 | * @default extend/queries 54 | */ 55 | extendDir?: string 56 | 57 | /** 58 | * The predefined queries & the user defined queries will be merged and placed in the queries output folder 59 | * 60 | * relative to the src dir of your nuxt app 61 | * 62 | * @default queries 63 | */ 64 | outputDir?: string 65 | } 66 | 67 | export type WPNuxtQuery = { 68 | name: string 69 | nodes?: string[] 70 | fragments: string[] 71 | params: Record 72 | operation: OperationTypeNode 73 | } 74 | -------------------------------------------------------------------------------- /src/useParser.ts: -------------------------------------------------------------------------------- 1 | import { parse } from 'graphql' 2 | import type { SelectionNode, OperationDefinitionNode } from 'graphql' 3 | import type { WPNuxtQuery } from './types' 4 | 5 | const _parseDoc = async (doc: string): Promise => { 6 | const { definitions } = parse(doc) 7 | 8 | const operations: WPNuxtQuery[] = definitions 9 | .filter(({ kind }) => kind === 'OperationDefinition') 10 | .map((definition) => { 11 | const operationDefinition = definition as OperationDefinitionNode 12 | if (!operationDefinition.name?.value) { 13 | throw new Error(`Operation name missing in: ${doc}`) 14 | } 15 | 16 | const query: WPNuxtQuery = { 17 | name: operationDefinition.name.value.trim(), 18 | nodes: [], 19 | fragments: [], 20 | params: {}, 21 | operation: operationDefinition.operation 22 | } 23 | processSelections(operationDefinition.selectionSet.selections, 0, query) 24 | return query 25 | }) 26 | return operations 27 | } 28 | 29 | function processSelections(selections: readonly SelectionNode[], level: number, query: WPNuxtQuery) { 30 | if (!selections || selections.length === 0) return 31 | if (selections.length === 1 && selections[0]?.kind === 'Field') { 32 | query.nodes?.push(selections[0].name.value.trim()) 33 | } 34 | selections.forEach((s) => { 35 | if (s.kind === 'FragmentSpread') { 36 | query.fragments?.push(s.name.value.trim()) 37 | } else if (s.selectionSet?.selections) { 38 | processSelections(s.selectionSet.selections, level + 1, query) 39 | } 40 | }) 41 | } 42 | 43 | export const parseDoc = _parseDoc 44 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import { existsSync, cpSync, promises as fsp } from 'node:fs' 3 | import { type LogLevel, createConsola } from 'consola' 4 | import { ref } from 'vue' 5 | import { hasNuxtModule, addTemplate, createResolver } from '@nuxt/kit' 6 | import { join } from 'pathe' 7 | import type { WPNuxtConfig } from './types' 8 | 9 | const loggerRef = ref() 10 | 11 | export const initLogger = (logLevel: LogLevel | undefined) => { 12 | loggerRef.value = createConsola({ 13 | level: logLevel ? logLevel : 3, 14 | formatOptions: { 15 | colors: true, 16 | compact: true, 17 | date: true, 18 | fancy: true 19 | } 20 | }).withTag('wpnuxt') 21 | return loggerRef.value 22 | } 23 | 24 | export const getLogger = () => { 25 | if (loggerRef.value) { 26 | return loggerRef.value 27 | } 28 | } 29 | 30 | /** 31 | * Validate the module options. 32 | */ 33 | export function validateConfig(options: Partial) { 34 | if (!options.wordpressUrl || options.wordpressUrl.length === 0) { 35 | throw new Error('WPNuxt error: WordPress url is missing') 36 | } else if (options.wordpressUrl.substring(options.wordpressUrl.length - 1) === '/') { 37 | throw new Error('WPNuxt error: WordPress url should not have a trailing slash: ' + options.wordpressUrl) 38 | } 39 | if (options.frontendUrl && options.frontendUrl.substring(options.frontendUrl.length - 1) === '/') { 40 | throw new Error('WPNuxt error: frontend url should not have a trailing slash: ' + options.frontendUrl) 41 | } 42 | } 43 | 44 | export async function mergeQueries(nuxt: any) { 45 | const { resolve } = createResolver(import.meta.url) 46 | const resolveRuntimeModule = (path: string) => resolve('./runtime', path) 47 | const logger = getLogger() 48 | 49 | const queryOutputPath = resolve((nuxt.options.srcDir || nuxt.options.rootDir) + '/.queries/') 50 | await fsp.rm(queryOutputPath, { recursive: true, force: true }) 51 | 52 | const userQueryPath = '~/extend/queries/' 53 | .replace(/^(~~|@@)/, nuxt.options.rootDir) 54 | .replace(/^(~|@)/, nuxt.options.srcDir) 55 | const userQueryPathExists = existsSync(userQueryPath) 56 | cpSync(resolveRuntimeModule('./queries/'), queryOutputPath, { recursive: true }) 57 | 58 | addQueriesFromAddOnModule('@wpnuxt/blocks', queryOutputPath, nuxt, resolve) 59 | addQueriesFromAddOnModule('@wpnuxt/auth', queryOutputPath, nuxt, resolve) 60 | 61 | if (userQueryPathExists) { 62 | logger.debug('Extending queries:', userQueryPath) 63 | cpSync(resolve(userQueryPath), queryOutputPath, { recursive: true }) 64 | } 65 | logger.debug('Copied merged queries in folder:', queryOutputPath) 66 | return queryOutputPath 67 | } 68 | 69 | // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type 70 | function addQueriesFromAddOnModule(addOnModuleName: string, queryOutputPath: string, nuxt: any, resolve: Function) { 71 | const logger = getLogger() 72 | if (hasNuxtModule(addOnModuleName)) { 73 | for (const m of nuxt.options._installedModules) { 74 | if (m.meta.name === addOnModuleName && m.entryPath) { 75 | logger.debug('Loading queries from ' + addOnModuleName) 76 | let blocksQueriesPath 77 | if (m.entryPath == '../src/module') { 78 | blocksQueriesPath = join(nuxt.options.rootDir, '../src/runtime/queries/') 79 | } else if (m.entryPath.startsWith('../')) { 80 | blocksQueriesPath = join(nuxt.options.rootDir, '../', m.entryPath, './runtime/queries/') 81 | } else { 82 | blocksQueriesPath = join('./node_modules', m.entryPath, 'dist/runtime/queries/') 83 | } 84 | cpSync(blocksQueriesPath, queryOutputPath, { recursive: true }) 85 | } 86 | } 87 | } else { 88 | const moduleName = addOnModuleName.replace('@', '') 89 | // TODO: (should we?) find a way to avoid this hack. (it makes sure the dynamic import in WPContent doesn't throw an error) 90 | addTemplate({ 91 | write: true, 92 | filename: moduleName + '.mjs', 93 | getContents: () => `export { }` 94 | }) 95 | nuxt.options.alias['#' + moduleName] = resolve(nuxt.options.buildDir, moduleName) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tests/basic.test.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url' 2 | import { describe, expect, it } from 'vitest' 3 | import { $fetch, setup, startServer } from '@nuxt/test-utils/e2e' 4 | 5 | describe('ssr', async () => { 6 | await setup({ 7 | rootDir: fileURLToPath(new URL('./fixtures/basic', import.meta.url)) 8 | }) 9 | 10 | it('renders the index page', async () => { 11 | // Get response to a server-rendered page with `$fetch`. 12 | const html = await $fetch('/') 13 | expect(html).toContain('
basic original value
') 14 | }) 15 | 16 | it('changes runtime config and restarts', async () => { 17 | await startServer({ env: { NUXT_PUBLIC_MY_VALUE: 'overwritten by test!' } }) 18 | 19 | const html = await $fetch('/') 20 | expect(html).toContain('
basic overwritten by test!
') 21 | 22 | await startServer() 23 | const htmlRestored = await $fetch('/') 24 | expect(htmlRestored).toContain('
basic original value
') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /tests/fixtures/basic/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | import WPNuxtModule from '../../../src/module' 2 | 3 | export default defineNuxtConfig({ 4 | modules: [ 5 | WPNuxtModule 6 | ], 7 | wpNuxt: { 8 | wordpressUrl: 'https://wordpress.wpnuxt.com', 9 | frontendUrl: 'https://demo.wpnuxt.com' 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /tests/fixtures/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "basic", 4 | "type": "module" 5 | } 6 | -------------------------------------------------------------------------------- /tests/fixtures/basic/pages/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nuxt/tsconfig.json", 3 | "exclude": [ 4 | "node_modules", 5 | "dist", 6 | "wordpress" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /wordpress/demo-content/newmodule.txt: -------------------------------------------------------------------------------- 1 | WPNuwt is a new Nuxt module to make it easy to set up a headless WordPress with a Nuxt frontend. 2 | 3 | Find out more at WPNuxt.com 4 | -------------------------------------------------------------------------------- /wordpress/demo-content/testpage.txt: -------------------------------------------------------------------------------- 1 | This is my test page 2 |

3 | Content is loaded from the file demo-content/testpage.txt 4 | -------------------------------------------------------------------------------- /wordpress/demo-content/testpost.txt: -------------------------------------------------------------------------------- 1 | This is my test post 2 |

3 | Content is loaded from the file demo-content/testpost-content.txt 4 | -------------------------------------------------------------------------------- /wordpress/wp-env-apache-modules.yml: -------------------------------------------------------------------------------- 1 | apache_modules: 2 | - mod_rewrite 3 | -------------------------------------------------------------------------------- /wordpress/wp-env-cli.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "-------------------- executing wpInstall script --------------------"; 3 | 4 | #wp-env run cli update --yes; 5 | #wp-env run cli wp core update; 6 | cli_version=$(wp-env run cli wp --version); 7 | echo "WordPress cli version: $cli_version"; 8 | wp_version=$(wp-env run cli wp core version); 9 | echo "WordPress version: $wp_version"; 10 | 11 | if [ $(wp-env run cli wp option list --search=wpnuxt_installed --format=count) == 1 ]; then 12 | echo "WPNuxt is already installed."; 13 | else 14 | wp-env run cli wp option update blogname "WPNuxt Demo"; 15 | 16 | wp-env run cli wp option add graphql_general_settings {} --format=json; 17 | wp-env run cli wp option patch insert graphql_general_settings public_introspection_enabled on; 18 | wp-env run cli wp option patch insert graphql_general_settings show_graphiql_link_in_admin_bar off; 19 | 20 | wp-env run cli wp option patch insert faustwp_settings frontend_uri "http://localhost:3000"; 21 | wp-env run cli wp option patch insert faustwp_settings disable_theme 1; 22 | wp-env run cli wp option patch insert faustwp_settings enable_redirects 1; 23 | wp-env run cli wp option patch insert faustwp_settings enable_rewrites 1; 24 | wp-env run cli wp option patch insert faustwp_settings enable_telemetry 1; 25 | wp-env run cli wp option patch insert faustwp_settings enable_image_source 1; 26 | wp-env run cli wp option patch insert faustwp_settings secret_key 64489e3c-1166-498a-9a6e-51cbb4c14ab2; 27 | 28 | wp-env run cli wp theme delete --all; 29 | 30 | wp-env run cli wp menu create main; 31 | wp-env run cli wp menu item add-post main $(wp-env run cli wp post list --post_type=page --field="ID" --name="sample-page") --title="Sample Page"; 32 | test_post_id="$(wp-env run cli wp post create ./demo-content/testpage.txt --post_type=page --post_title="Test Page" --post_status=publish --porcelain)"; 33 | wp-env run cli wp menu item add-post main $test_post_id --title="Test Page"; 34 | wp-env run cli wp menu location assign main primary; 35 | wp-env run cli wp post create ./demo-content/testpost.txt --post_type=post --post_title="Test Post" --post_status=publish 36 | wp-env run cli wp post create ./demo-content/newmodule.txt --post_type=post --post_title="A new Nuxt module" --post_status=publish 37 | 38 | wp-env run cli wp rewrite flush; 39 | 40 | wp-env run cli wp option add wpnuxt_installed 1; 41 | 42 | echo "----------------- WPNuxt installed successfully: --------------------"; 43 | fi 44 | --------------------------------------------------------------------------------