From 18c8f88acbf7855326952ead3c6deb463f140546 Mon Sep 17 00:00:00 2001
From: Niklas Wolf
Date: Mon, 17 Jun 2024 15:10:46 +0200
Subject: [PATCH 1/8] feat: add possibility to not load Mollie script globally
---
package.json | 3 +-
src/module.ts | 14 ++++++--
src/runtime/composables/useMollie.ts | 10 ++++--
src/runtime/plugins/plugin.client.ts | 53 +++++++++++++++++++++++-----
src/runtime/plugins/plugin.server.ts | 21 ++++++-----
src/types.d.ts | 8 +++--
6 files changed, 84 insertions(+), 25 deletions(-)
diff --git a/package.json b/package.json
index e4011e0..6c9240c 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,8 @@
"prettier-check": "pnpm exec prettier . --check"
},
"dependencies": {
- "@nuxt/kit": "^3.8.2"
+ "@nuxt/kit": "^3.8.2",
+ "defu": "^6.1.4"
},
"devDependencies": {
"@nuxt/devtools": "latest",
diff --git a/src/module.ts b/src/module.ts
index b4c2173..63f1b77 100644
--- a/src/module.ts
+++ b/src/module.ts
@@ -1,15 +1,25 @@
import { defineNuxtModule, addPlugin, addImports, createResolver, addComponent } from '@nuxt/kit'
import type { MollieOptions } from './types'
+import { defu } from 'defu'
export default defineNuxtModule({
meta: {
name: 'nuxt-mollie-payments-components',
- configKey: 'nuxtMolliePaymentsComponents',
+ configKey: 'molliePaymentsComponents',
+ },
+ defaults: {
+ profileId: '',
+ defaultLocale: 'en_US',
+ testMode: false,
+ includeScriptGlobally: true,
},
async setup(options, nuxt) {
const resolver = createResolver(import.meta.url)
- nuxt.options.runtimeConfig.public.mollie ||= options
+ nuxt.options.runtimeConfig.public.molliePaymentsComponents = defu(
+ nuxt.options.runtimeConfig.public.molliePaymentsComponents,
+ options,
+ )
addPlugin({
src: resolver.resolve('./runtime/plugins/plugin.server'),
diff --git a/src/runtime/composables/useMollie.ts b/src/runtime/composables/useMollie.ts
index 126bcc8..8eb9af9 100644
--- a/src/runtime/composables/useMollie.ts
+++ b/src/runtime/composables/useMollie.ts
@@ -1,4 +1,4 @@
-import { useNuxtApp } from '#imports'
+import { useNuxtApp, ref } from '#imports'
import type { CreateLocaleInstanceArgs } from '../../types'
/**
@@ -6,9 +6,14 @@ import type { CreateLocaleInstanceArgs } from '../../types'
*/
export function useMollie(args: CreateLocaleInstanceArgs = {}) {
const { $mollie } = useNuxtApp()
+ const isInitialized = ref(false)
async function init() {
- $mollie.createMollieInstance(args)
+ // Wait for scripts to be loaded, then initialize the mollie instance.
+ await $mollie.scriptLoadedPromise.then(() => {
+ $mollie.createMollieInstance(args)
+ isInitialized.value = true
+ })
}
async function getToken(): Promise {
@@ -23,5 +28,6 @@ export function useMollie(args: CreateLocaleInstanceArgs = {}) {
return {
init,
getToken,
+ isInitialized,
}
}
diff --git a/src/runtime/plugins/plugin.client.ts b/src/runtime/plugins/plugin.client.ts
index 14ef8c8..542e0ca 100644
--- a/src/runtime/plugins/plugin.client.ts
+++ b/src/runtime/plugins/plugin.client.ts
@@ -1,22 +1,58 @@
import { defineNuxtPlugin } from '#app'
-import { useRuntimeConfig } from '#imports'
-import type { CreateLocaleInstanceArgs, MolliePlugin, MollieOptions } from '../../types'
+import { useHead, useRuntimeConfig } from '#imports'
+import type { CreateLocaleInstanceArgs, MollieOptions, MolliePlugin } from '../../types'
+
+/**
+ * Get a promise with its resolvers.
+ * This can be replaced by native JS when browser support is better
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
+ */
+function getPromiseWithResolvers() {
+ let resolve: (value: T | PromiseLike) => void = () => {}
+ let reject: (value: T | PromiseLike) => void = () => {}
+ const promise = new Promise((res, rej) => {
+ resolve = res
+ reject = rej
+ })
+
+ return { promise, resolve, reject }
+}
export default defineNuxtPlugin({
name: 'mollie-instance',
enforce: 'pre',
async setup(nuxtApp) {
- if (!window.Mollie) {
- console.error(
- "mollie-register plugin didn't register required scripts, thus mollie instance cannot be created.",
- )
+ /*
+ * Register mollie-script client-side for client-side rendering.
+ * If SSR is used, this will NOT register a second script, because it was already registered server side (see server-plugin)
+ */
+ const { promise: scriptLoadedPromise, resolve } = getPromiseWithResolvers()
+ if (window.Mollie) {
+ resolve(true)
+ } else {
+ useHead({
+ script: [
+ {
+ src: 'https://js.mollie.com/v1/mollie.js',
+ defer: true,
+ onload() {
+ resolve(true)
+ },
+ },
+ ],
+ })
}
- const runtimeConfig = useRuntimeConfig()
+ const runtimeConfig = useRuntimeConfig()
const mollieOptions = runtimeConfig?.public?.mollie as MollieOptions
function createLocaleInstance(args: CreateLocaleInstanceArgs) {
+ if (!window.Mollie) {
+ console.error(
+ "mollie-register plugin didn't register required scripts, thus mollie instance cannot be created.",
+ )
+ }
if (!args) {
args = {
profileId: mollieOptions.profileId,
@@ -27,7 +63,7 @@ export default defineNuxtPlugin({
const { profileId, testMode, locale } = args
- return profileId
+ return profileId && window.Mollie
? window.Mollie(profileId, {
locale: locale ?? 'en_US',
testmode: typeof testMode === 'boolean' ? testMode : true,
@@ -41,6 +77,7 @@ export default defineNuxtPlugin({
this.mollieInstance = this.mollieInstance || createLocaleInstance(args)
return this.mollieInstance
},
+ scriptLoadedPromise,
}
nuxtApp.provide('mollie', Mollie)
diff --git a/src/runtime/plugins/plugin.server.ts b/src/runtime/plugins/plugin.server.ts
index a002dc8..f0c2be2 100644
--- a/src/runtime/plugins/plugin.server.ts
+++ b/src/runtime/plugins/plugin.server.ts
@@ -1,18 +1,21 @@
import { defineNuxtPlugin } from '#app'
-import { useServerHead } from '#imports'
+import { useServerHead, useRuntimeConfig } from '#imports'
export default defineNuxtPlugin({
name: 'mollie-register',
enforce: 'pre',
async setup() {
- useServerHead({
- script: [
- {
- src: 'https://js.mollie.com/v1/mollie.js',
- defer: true,
- },
- ],
- })
+ const config = useRuntimeConfig().public.molliePaymentsComponents
+ if (config.includeScriptGlobally) {
+ useServerHead({
+ script: [
+ {
+ src: 'https://js.mollie.com/v1/mollie.js',
+ defer: true,
+ },
+ ],
+ })
+ }
},
})
diff --git a/src/types.d.ts b/src/types.d.ts
index 3eb9426..ac8065d 100644
--- a/src/types.d.ts
+++ b/src/types.d.ts
@@ -23,13 +23,14 @@ export type CreateLocaleInstanceArgs = {
declare global {
interface Window {
- Mollie: Mollie
+ Mollie?: Mollie
}
}
export type MolliePlugin = {
mollieInstance: ReturnType | null
createMollieInstance(args: CreateLocaleInstanceArgs): void
+ scriptLoadedPromise: Promise
}
declare module '#app' {
interface NuxtApp {
@@ -82,6 +83,7 @@ export type MollieOptions = {
profileId: string
defaultLocale: MollieLocale
testMode: boolean
+ includeScriptGlobally?: boolean
}
export type MollieConfig = {
@@ -116,11 +118,11 @@ export type MollieCreditCardMandate = {
declare module 'nuxt/schema' {
interface NuxtConfig {
- mollie?: MollieOptions
+ molliePaymentsComponents?: MollieOptions
}
interface PublicRuntimeConfig {
- mollie: MollieOptions
+ molliePaymentsComponents: MollieOptions
}
}
From 5e1a3672402b2996f27f124e70a32143378aaaf9 Mon Sep 17 00:00:00 2001
From: Niklas Wolf
Date: Mon, 17 Jun 2024 15:35:23 +0200
Subject: [PATCH 2/8] feat: expose function from useMollie composable to load
the Mollie script async
---
playground/app.vue | 67 +--------------------------
playground/nuxt.config.ts | 4 +-
playground/pages/index.vue | 68 ++++++++++++++++++++++++++++
playground/pages/without.vue | 36 +++++++++++++++
src/runtime/composables/useMollie.ts | 2 +-
src/runtime/plugins/plugin.client.ts | 36 ++++++++-------
src/types.d.ts | 2 +-
7 files changed, 130 insertions(+), 85 deletions(-)
create mode 100644 playground/pages/index.vue
create mode 100644 playground/pages/without.vue
diff --git a/playground/app.vue b/playground/app.vue
index 9fc4dbe..ea4ef5c 100644
--- a/playground/app.vue
+++ b/playground/app.vue
@@ -1,68 +1,3 @@
-
- Nuxt mollie payments components
-
- Credit Card
-
- {
- CreditCardToken = `${token} ✔️`
- CreditCardError = null
- }
- "
- @error="
- (message) => {
- CreditCardError = `${message} ❌`
- }
- "
- />
-
-
-
- Test Credit Number:
-
2223 0000 1047 9399
-
-
-
Error: {{ CreditCardError }}
-
Token: {{ CreditCardToken }}
-
-
- Ideal
-
- {
- IdealError = null
- }
- "
- @save-issuer-error="
- (message) => {
- IdealError = `${message} ❌`
- }
- "
- />
+
-
diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts
index f69c1b6..d80243b 100644
--- a/playground/nuxt.config.ts
+++ b/playground/nuxt.config.ts
@@ -1,4 +1,5 @@
export default defineNuxtConfig({
+ ssr: true,
extends: ['@shopware-pwa/composables-next/nuxt-layer'],
modules: ['../src/module', '@shopware-pwa/nuxt3-module'],
runtimeConfig: {
@@ -10,10 +11,11 @@ export default defineNuxtConfig({
},
},
/* config not needed with mollie > v4.4.0 */
- mollie: {
+ molliePaymentsComponents: {
defaultLocale: 'en_US',
profileId: 'pfl_E5EmGZ98YT',
testMode: true,
+ includeScriptGlobally: false,
},
devtools: { enabled: true },
})
diff --git a/playground/pages/index.vue b/playground/pages/index.vue
new file mode 100644
index 0000000..9fc4dbe
--- /dev/null
+++ b/playground/pages/index.vue
@@ -0,0 +1,68 @@
+
+
+ Nuxt mollie payments components
+
+ Credit Card
+
+ {
+ CreditCardToken = `${token} ✔️`
+ CreditCardError = null
+ }
+ "
+ @error="
+ (message) => {
+ CreditCardError = `${message} ❌`
+ }
+ "
+ />
+
+
+
+ Test Credit Number:
+
2223 0000 1047 9399
+
+
+
Error: {{ CreditCardError }}
+
Token: {{ CreditCardToken }}
+
+
+ Ideal
+
+ {
+ IdealError = null
+ }
+ "
+ @save-issuer-error="
+ (message) => {
+ IdealError = `${message} ❌`
+ }
+ "
+ />
+
+
diff --git a/playground/pages/without.vue b/playground/pages/without.vue
new file mode 100644
index 0000000..fa84479
--- /dev/null
+++ b/playground/pages/without.vue
@@ -0,0 +1,36 @@
+
+
+
+ Load Mollie script lazily
+ On this page the scripts load lazily, if the button is clicked.
+ To test this, the option isIncludedGlobally: false has to be set in nuxt config.
+
+
+
+
+ {
+ CreditCardToken = `${token} ✔️`
+ CreditCardError = null
+ }
+ "
+ @error="
+ (message) => {
+ CreditCardError = `${message} ❌`
+ }
+ "
+ />
+
+
+
+
+
diff --git a/src/runtime/composables/useMollie.ts b/src/runtime/composables/useMollie.ts
index 8eb9af9..79698a9 100644
--- a/src/runtime/composables/useMollie.ts
+++ b/src/runtime/composables/useMollie.ts
@@ -10,7 +10,7 @@ export function useMollie(args: CreateLocaleInstanceArgs = {}) {
async function init() {
// Wait for scripts to be loaded, then initialize the mollie instance.
- await $mollie.scriptLoadedPromise.then(() => {
+ await $mollie.loadMollieScript().then(() => {
$mollie.createMollieInstance(args)
isInitialized.value = true
})
diff --git a/src/runtime/plugins/plugin.client.ts b/src/runtime/plugins/plugin.client.ts
index 542e0ca..2e2783a 100644
--- a/src/runtime/plugins/plugin.client.ts
+++ b/src/runtime/plugins/plugin.client.ts
@@ -23,25 +23,29 @@ export default defineNuxtPlugin({
enforce: 'pre',
async setup(nuxtApp) {
- /*
+ /**
* Register mollie-script client-side for client-side rendering.
* If SSR is used, this will NOT register a second script, because it was already registered server side (see server-plugin)
*/
- const { promise: scriptLoadedPromise, resolve } = getPromiseWithResolvers()
- if (window.Mollie) {
- resolve(true)
- } else {
- useHead({
- script: [
- {
- src: 'https://js.mollie.com/v1/mollie.js',
- defer: true,
- onload() {
- resolve(true)
+ function loadMollieScript() {
+ const { promise, resolve } = getPromiseWithResolvers()
+ if (window.Mollie) {
+ resolve(true)
+ } else {
+ useHead({
+ script: [
+ {
+ src: 'https://js.mollie.com/v1/mollie.js',
+ defer: true,
+ onload() {
+ resolve(true)
+ },
},
- },
- ],
- })
+ ],
+ })
+ }
+
+ return promise
}
const runtimeConfig = useRuntimeConfig()
@@ -77,7 +81,7 @@ export default defineNuxtPlugin({
this.mollieInstance = this.mollieInstance || createLocaleInstance(args)
return this.mollieInstance
},
- scriptLoadedPromise,
+ loadMollieScript,
}
nuxtApp.provide('mollie', Mollie)
diff --git a/src/types.d.ts b/src/types.d.ts
index ac8065d..a8433ba 100644
--- a/src/types.d.ts
+++ b/src/types.d.ts
@@ -30,7 +30,7 @@ declare global {
export type MolliePlugin = {
mollieInstance: ReturnType | null
createMollieInstance(args: CreateLocaleInstanceArgs): void
- scriptLoadedPromise: Promise
+ loadMollieScript: () => Promise
}
declare module '#app' {
interface NuxtApp {
From 48924ebc894c5dd3006db23b8586c1cc7291e0ea Mon Sep 17 00:00:00 2001
From: Niklas Wolf
Date: Mon, 17 Jun 2024 15:42:04 +0200
Subject: [PATCH 3/8] feat: adjust tests
---
test/useMollie.test.ts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/test/useMollie.test.ts b/test/useMollie.test.ts
index 57568cd..ea40fbb 100644
--- a/test/useMollie.test.ts
+++ b/test/useMollie.test.ts
@@ -4,6 +4,7 @@ import { useMollie } from './src/runtime/composables/useMollie'
vi.mock('#imports', () => ({
useNuxtApp: vi.fn(),
+ ref: vi.fn((initialValue) => ({ value: initialValue })),
}))
describe('useMollie', () => {
@@ -23,6 +24,7 @@ describe('useMollie', () => {
mollieInstance: {
createToken: mockCreateToken,
},
+ loadMollieScript: vi.fn().mockResolvedValue(true),
},
}
From 5a70daf677f7042999d4dc22fa9fde96cefc5347 Mon Sep 17 00:00:00 2001
From: Niklas Wolf
Date: Mon, 17 Jun 2024 15:48:10 +0200
Subject: [PATCH 4/8] fix: use global loading in playground per default
---
playground/nuxt.config.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts
index d80243b..daf4129 100644
--- a/playground/nuxt.config.ts
+++ b/playground/nuxt.config.ts
@@ -15,7 +15,7 @@ export default defineNuxtConfig({
defaultLocale: 'en_US',
profileId: 'pfl_E5EmGZ98YT',
testMode: true,
- includeScriptGlobally: false,
+ includeScriptGlobally: true,
},
devtools: { enabled: true },
})
From 6b40ab4e20f455d634ad842147d1433ea0a68e3a Mon Sep 17 00:00:00 2001
From: Niklas Wolf
Date: Mon, 17 Jun 2024 16:09:34 +0200
Subject: [PATCH 5/8] fix error while building package
---
src/module.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/module.ts b/src/module.ts
index 63f1b77..0500d62 100644
--- a/src/module.ts
+++ b/src/module.ts
@@ -17,7 +17,7 @@ export default defineNuxtModule({
async setup(options, nuxt) {
const resolver = createResolver(import.meta.url)
nuxt.options.runtimeConfig.public.molliePaymentsComponents = defu(
- nuxt.options.runtimeConfig.public.molliePaymentsComponents,
+ nuxt.options.runtimeConfig.public.molliePaymentsComponents as MollieOptions,
options,
)
From 61a637e34f9cb925a9835459b2b4bcb8a2805fdb Mon Sep 17 00:00:00 2001
From: Niklas Wolf
Date: Fri, 21 Jun 2024 11:46:16 +0200
Subject: [PATCH 6/8] fix linter errors
---
playground/pages/index.vue | 86 ++++++++++++++++++------------------
playground/pages/without.vue | 53 +++++++++++-----------
2 files changed, 72 insertions(+), 67 deletions(-)
diff --git a/playground/pages/index.vue b/playground/pages/index.vue
index 9fc4dbe..ba7b68a 100644
--- a/playground/pages/index.vue
+++ b/playground/pages/index.vue
@@ -6,54 +6,56 @@ const CreditCardToken = ref()
const IdealError = ref()
- Nuxt mollie payments components
+
+
Nuxt mollie payments components
-
Credit Card
+
Credit Card
-
{
- CreditCardToken = `${token} ✔️`
- CreditCardError = null
- }
- "
- @error="
- (message) => {
- CreditCardError = `${message} ❌`
- }
- "
- />
+ {
+ CreditCardToken = `${token} ✔️`
+ CreditCardError = null
+ }
+ "
+ @error="
+ (message) => {
+ CreditCardError = `${message} ❌`
+ }
+ "
+ />
-
-
- Test Credit Number:
-
2223 0000 1047 9399
+
+
+ Test Credit Number:
+
2223 0000 1047 9399
+
+
+
Error: {{ CreditCardError }}
+
Token: {{ CreditCardToken }}
-
-
Error: {{ CreditCardError }}
-
Token: {{ CreditCardToken }}
-
-
Ideal
+
Ideal
-
{
- IdealError = null
- }
- "
- @save-issuer-error="
- (message) => {
- IdealError = `${message} ❌`
- }
- "
- />
+ {
+ IdealError = null
+ }
+ "
+ @save-issuer-error="
+ (message) => {
+ IdealError = `${message} ❌`
+ }
+ "
+ />
+
From 49e377f3621a8d7ab41e40ca07513eabd3e256d3 Mon Sep 17 00:00:00 2001
From: Niklas Wolf
Date: Fri, 21 Jun 2024 11:51:02 +0200
Subject: [PATCH 7/8] feat: add navbar for different pages to playground
---
playground/components/NavigationBar.vue | 15 +++++++++++++++
playground/pages/index.vue | 2 ++
playground/pages/without.vue | 3 +++
3 files changed, 20 insertions(+)
create mode 100644 playground/components/NavigationBar.vue
diff --git a/playground/components/NavigationBar.vue b/playground/components/NavigationBar.vue
new file mode 100644
index 0000000..15796a5
--- /dev/null
+++ b/playground/components/NavigationBar.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/playground/pages/index.vue b/playground/pages/index.vue
index ba7b68a..450e612 100644
--- a/playground/pages/index.vue
+++ b/playground/pages/index.vue
@@ -1,5 +1,6 @@
+
Nuxt mollie payments components
Credit Card
diff --git a/playground/pages/without.vue b/playground/pages/without.vue
index f31d4da..885086d 100644
--- a/playground/pages/without.vue
+++ b/playground/pages/without.vue
@@ -1,5 +1,6 @@