diff --git a/examples/nuxt3/app.vue b/examples/nuxt3/app.vue
index 4380aa37..af399614 100644
--- a/examples/nuxt3/app.vue
+++ b/examples/nuxt3/app.vue
@@ -2,6 +2,8 @@
Global directive
Expect text with color
Expect only plaintext
+
+
Directive local to a component
Expect text with color
@@ -12,5 +14,7 @@ import { buildVueDompurifyHTMLDirective } from 'vue-dompurify-html';
const rawHtml = ref('Hello!');
+const ssrHtml = useState(() => 'Hello SSR!');
+
const vCleanHtml = buildVueDompurifyHTMLDirective();
diff --git a/examples/nuxt3/nuxt.config.ts b/examples/nuxt3/nuxt.config.ts
index 231ffd7d..d21d85e8 100644
--- a/examples/nuxt3/nuxt.config.ts
+++ b/examples/nuxt3/nuxt.config.ts
@@ -1,3 +1,4 @@
export default defineNuxtConfig({
devtools: { enabled: true },
+ compatibilityDate: '2025-01-10',
});
diff --git a/packages/vue-dompurify-html/dist/vue-dompurify-html.mjs b/packages/vue-dompurify-html/dist/vue-dompurify-html.mjs
index 06640703..99b750a8 100644
--- a/packages/vue-dompurify-html/dist/vue-dompurify-html.mjs
+++ b/packages/vue-dompurify-html/dist/vue-dompurify-html.mjs
@@ -1,50 +1,50 @@
-import l from "dompurify";
-function m(t, e) {
- const o = t.hooks ?? {};
- let n;
- for (n in o) {
- const u = o[n];
- u !== void 0 && e.addHook(n, u);
+import c from "isomorphic-dompurify";
+function f(n, r) {
+ const t = n.hooks ?? {};
+ let o;
+ for (o in t) {
+ const e = t[o];
+ e !== void 0 && r.addHook(o, e);
}
}
-function c() {
- return l();
+function d() {
+ return c();
}
-function p(t = {}, e = c) {
- const o = e();
- m(t, o);
- const n = function(u, i) {
- const r = i.value;
- if (i.oldValue === r)
- return;
- const a = `${r}`, s = i.arg, d = t.namedConfigurations, f = t.default ?? {};
- if (d && s !== void 0) {
- u.innerHTML = o.sanitize(
- a,
- d[s] ?? f
- );
- return;
- }
- u.innerHTML = o.sanitize(
- a,
- f
- );
+function a(n, r = {}, t) {
+ const o = n.value;
+ if (n.oldValue === o)
+ return;
+ const e = `${o}`, i = n.arg, u = r.namedConfigurations, s = r.default ?? {};
+ return u && i !== void 0 ? t.sanitize(
+ e,
+ u[i] ?? s
+ ) : t.sanitize(e, s);
+}
+function l(n = {}, r = d) {
+ const t = r();
+ f(n, t);
+ const o = function(e, i) {
+ const u = a(i, n, t);
+ u !== void 0 && (e.innerHTML = u);
};
return {
- mounted: n,
- updated: n
+ mounted: o,
+ updated: o,
+ getSSRProps: (e) => ({
+ innerHTML: a(e, n, c)
+ })
};
}
-const k = {
- install(t, e = {}, o = c) {
- t.directive(
+const p = {
+ install(n, r = {}, t = d) {
+ n.directive(
"dompurify-html",
- p(e, o)
+ l(r, t)
);
}
};
export {
- p as buildVueDompurifyHTMLDirective,
- k as default,
- k as vueDompurifyHTMLPlugin
+ l as buildVueDompurifyHTMLDirective,
+ p as default,
+ p as vueDompurifyHTMLPlugin
};
diff --git a/packages/vue-dompurify-html/dist/vue-dompurify-html.umd.js b/packages/vue-dompurify-html/dist/vue-dompurify-html.umd.js
index d0d21fd5..deb439e2 100644
--- a/packages/vue-dompurify-html/dist/vue-dompurify-html.umd.js
+++ b/packages/vue-dompurify-html/dist/vue-dompurify-html.umd.js
@@ -1 +1 @@
-(function(e,i){typeof exports=="object"&&typeof module<"u"?i(exports,require("dompurify")):typeof define=="function"&&define.amd?define(["exports","dompurify"],i):(e=typeof globalThis<"u"?globalThis:e||self,i(e.VueDOMPurifyHTML={},e.DOMPurify))})(this,function(e,i){"use strict";function v(t,o){const n=t.hooks??{};let u;for(u in n){const r=n[u];r!==void 0&&o.addHook(u,r)}}function d(){return i()}function s(t={},o=d){const n=o();v(t,n);const u=function(r,f){const c=f.value;if(f.oldValue===c)return;const l=`${c}`,m=f.arg,p=t.namedConfigurations,y=t.default??{};if(p&&m!==void 0){r.innerHTML=n.sanitize(l,p[m]??y);return}r.innerHTML=n.sanitize(l,y)};return{mounted:u,updated:u}}const a={install(t,o={},n=d){t.directive("dompurify-html",s(o,n))}};e.buildVueDompurifyHTMLDirective=s,e.default=a,e.vueDompurifyHTMLPlugin=a,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
+(function(t,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("isomorphic-dompurify")):typeof define=="function"&&define.amd?define(["exports","isomorphic-dompurify"],r):(t=typeof globalThis<"u"?globalThis:t||self,r(t.VueDOMPurifyHTML={},t.dompurify))})(this,function(t,r){"use strict";function m(e,u){const n=e.hooks??{};let o;for(o in n){const i=n[o];i!==void 0&&u.addHook(o,i)}}function d(){return r()}function c(e,u={},n){const o=e.value;if(e.oldValue===o)return;const i=`${o}`,f=e.arg,s=u.namedConfigurations,p=u.default??{};return s&&f!==void 0?n.sanitize(i,s[f]??p):n.sanitize(i,p)}function a(e={},u=d){const n=u();m(e,n);const o=function(i,f){const s=c(f,e,n);s!==void 0&&(i.innerHTML=s)};return{mounted:o,updated:o,getSSRProps:i=>({innerHTML:c(i,e,r)})}}const l={install(e,u={},n=d){e.directive("dompurify-html",a(u,n))}};t.buildVueDompurifyHTMLDirective=a,t.default=l,t.vueDompurifyHTMLPlugin=l,Object.defineProperties(t,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
diff --git a/packages/vue-dompurify-html/package.json b/packages/vue-dompurify-html/package.json
index 7bf02297..37eedc30 100644
--- a/packages/vue-dompurify-html/package.json
+++ b/packages/vue-dompurify-html/package.json
@@ -45,7 +45,7 @@
"test-mutation": "stryker run"
},
"dependencies": {
- "dompurify": "^3.2.1"
+ "isomorphic-dompurify": "^2.20.0"
},
"peerDependencies": {
"vue": "^3.0.0"
diff --git a/packages/vue-dompurify-html/src/dompurify-html.ts b/packages/vue-dompurify-html/src/dompurify-html.ts
index 4d878d6c..3e1dd858 100644
--- a/packages/vue-dompurify-html/src/dompurify-html.ts
+++ b/packages/vue-dompurify-html/src/dompurify-html.ts
@@ -1,11 +1,11 @@
import type { DirectiveHook, ObjectDirective, DirectiveBinding } from 'vue';
-import dompurify from 'dompurify';
+import dompurify from 'isomorphic-dompurify';
import type {
DOMPurify,
UponSanitizeElementHookEvent,
UponSanitizeAttributeHookEvent,
HookName,
-} from 'dompurify';
+} from 'isomorphic-dompurify';
type MinimalDOMPurifyInstance = Pick;
export type DOMPurifyInstanceBuilder = () => MinimalDOMPurifyInstance;
@@ -112,9 +112,37 @@ function setUpHooks(
}
export function defaultDOMPurifyInstanceBuilder(): MinimalDOMPurifyInstance {
+ // @ts-expect-error This expression is not callable. Type 'typeof _default' has no call signatures.
return dompurify();
}
+function _getInnerHTML(
+ binding: DirectiveBinding,
+ config: DirectiveConfig = {},
+ dompurifyInstance: MinimalDOMPurifyInstance,
+): string | undefined {
+ const current_value = binding.value;
+ if (binding.oldValue === current_value) {
+ return;
+ }
+
+ const current_value_string = `${current_value}`;
+ const arg = binding.arg;
+ const namedConfigurations = config.namedConfigurations;
+ const defaultConfig = config.default ?? {};
+
+ // Returned named config
+ if (namedConfigurations && arg !== undefined) {
+ return dompurifyInstance.sanitize(
+ current_value_string,
+ namedConfigurations[arg] ?? defaultConfig,
+ );
+ }
+
+ // Returned default config
+ return dompurifyInstance.sanitize(current_value_string, defaultConfig);
+}
+
export function buildDirective(
config: DirectiveConfig = {},
buildDOMPurifyInstance: DOMPurifyInstanceBuilder = defaultDOMPurifyInstanceBuilder,
@@ -127,29 +155,22 @@ export function buildDirective(
el: HTMLElement,
binding: DirectiveBinding,
): void {
- const current_value = binding.value;
- if (binding.oldValue === current_value) {
- return;
- }
- const current_value_string = `${current_value}`;
- const arg = binding.arg;
- const namedConfigurations = config.namedConfigurations;
- const defaultConfig = config.default ?? {};
- if (namedConfigurations && arg !== undefined) {
- el.innerHTML = dompurifyInstance.sanitize(
- current_value_string,
- namedConfigurations[arg] ?? defaultConfig,
- );
- return;
+ const innerHTML = _getInnerHTML(binding, config, dompurifyInstance);
+
+ if (innerHTML !== undefined) {
+ el.innerHTML = innerHTML;
}
- el.innerHTML = dompurifyInstance.sanitize(
- current_value_string,
- defaultConfig,
- );
};
return {
mounted: updateComponent,
updated: updateComponent,
+ getSSRProps: (binding) => {
+ const innerHTML = _getInnerHTML(binding, config, dompurify);
+
+ return {
+ innerHTML,
+ };
+ },
};
}
diff --git a/packages/vue-dompurify-html/test/index.spec.ts b/packages/vue-dompurify-html/test/index.spec.ts
index 08b9bcd9..72ea6382 100644
--- a/packages/vue-dompurify-html/test/index.spec.ts
+++ b/packages/vue-dompurify-html/test/index.spec.ts
@@ -3,7 +3,7 @@ import { createApp } from 'vue';
import type { DOMPurifyInstanceBuilder } from '../src';
import VueDOMPurifyHTML from '../src';
import { buildVueDompurifyHTMLDirective } from '../src';
-import type { DOMPurify } from 'dompurify';
+import type { DOMPurify } from 'isomorphic-dompurify';
describe('VueDOMPurifyHTML Plugin Install', (): void => {
it('can be installed', (): void => {
diff --git a/packages/vue-dompurify-html/test/vue-dompurify-html.spec.ts b/packages/vue-dompurify-html/test/vue-dompurify-html.spec.ts
index 46f0031e..dc395595 100644
--- a/packages/vue-dompurify-html/test/vue-dompurify-html.spec.ts
+++ b/packages/vue-dompurify-html/test/vue-dompurify-html.spec.ts
@@ -3,11 +3,13 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
import { mount } from '@vue/test-utils';
import type { DOMPurifyInstanceBuilder } from '../src/dompurify-html';
import { buildDirective } from '../src/dompurify-html';
-import type { DOMPurify } from 'dompurify';
-import { sanitize, addHook } from 'dompurify';
+import type { DOMPurify } from 'isomorphic-dompurify';
+import { sanitize, addHook } from 'isomorphic-dompurify';
-vi.mock('dompurify', async () => {
- const actual: { default: DOMPurify } = await vi.importActual('dompurify');
+vi.mock('isomorphic-dompurify', async () => {
+ const actual: { default: DOMPurify } = await vi.importActual(
+ 'isomorphic-dompurify',
+ );
const spy = {
...actual,
sanitize: vi.fn(actual.default.sanitize),
diff --git a/packages/vue-dompurify-html/types/dompurify-html.d.ts b/packages/vue-dompurify-html/types/dompurify-html.d.ts
index 06817724..2cbe1f82 100755
--- a/packages/vue-dompurify-html/types/dompurify-html.d.ts
+++ b/packages/vue-dompurify-html/types/dompurify-html.d.ts
@@ -1,5 +1,5 @@
import type { ObjectDirective } from 'vue';
-import type { DOMPurify, UponSanitizeElementHookEvent, UponSanitizeAttributeHookEvent } from 'dompurify';
+import type { DOMPurify, UponSanitizeElementHookEvent, UponSanitizeAttributeHookEvent } from 'isomorphic-dompurify';
type MinimalDOMPurifyInstance = Pick;
export type DOMPurifyInstanceBuilder = () => MinimalDOMPurifyInstance;
export interface MinimalDOMPurifyConfig {
diff --git a/packages/vue-dompurify-html/vite.config.mts b/packages/vue-dompurify-html/vite.config.mts
index 8acfaba3..e4a0d99d 100644
--- a/packages/vue-dompurify-html/vite.config.mts
+++ b/packages/vue-dompurify-html/vite.config.mts
@@ -7,7 +7,7 @@ export default defineConfig({
name: 'VueDOMPurifyHTML',
},
rollupOptions: {
- external: ['dompurify'],
+ external: ['isomorphic-dompurify'],
output: {
globals: {
dompurify: 'DOMPurify',
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9281eafd..0909ad11 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -75,9 +75,9 @@ importers:
packages/vue-dompurify-html:
dependencies:
- dompurify:
- specifier: ^3.2.1
- version: 3.2.1
+ isomorphic-dompurify:
+ specifier: ^2.20.0
+ version: 2.20.0
devDependencies:
'@stryker-mutator/core':
specifier: 8.7.1
@@ -1980,8 +1980,8 @@ packages:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
- dompurify@3.2.1:
- resolution: {integrity: sha512-NBHEsc0/kzRYQd+AY6HR6B/IgsqzBABrqJbpCDQII/OK6h7B7LXzweZTDsqSW2LkTRpoxf18YUP+YjGySk6B3w==}
+ dompurify@3.2.3:
+ resolution: {integrity: sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==}
domutils@3.1.0:
resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
@@ -2607,6 +2607,10 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+ isomorphic-dompurify@2.20.0:
+ resolution: {integrity: sha512-zOq12fJPtNE+4dPd2S0xpWXl8NZj0C6k2xikT1yl/Lv/5p3QLafZqlVFy4xTGU9qHSkyEENcIbp2c0oahCNRYg==}
+ engines: {node: '>=18'}
+
istanbul-lib-coverage@3.2.2:
resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
engines: {node: '>=8'}
@@ -4426,7 +4430,7 @@ snapshots:
'@babel/traverse': 7.25.9
'@babel/types': 7.26.0
convert-source-map: 2.0.0
- debug: 4.3.7(supports-color@9.4.0)
+ debug: 4.3.7
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -4721,7 +4725,7 @@ snapshots:
'@babel/parser': 7.26.2
'@babel/template': 7.25.9
'@babel/types': 7.26.3
- debug: 4.3.7(supports-color@9.4.0)
+ debug: 4.3.7
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -5842,7 +5846,7 @@ snapshots:
dependencies:
'@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 0.2.3
- debug: 4.3.7(supports-color@9.4.0)
+ debug: 4.3.7
istanbul-lib-coverage: 3.2.2
istanbul-lib-report: 3.0.1
istanbul-lib-source-maps: 5.0.6
@@ -6094,6 +6098,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ agent-base@7.1.0:
+ dependencies:
+ debug: 4.3.7
+ transitivePeerDependencies:
+ - supports-color
+
agent-base@7.1.0(supports-color@9.4.0):
dependencies:
debug: 4.3.7(supports-color@9.4.0)
@@ -6525,6 +6535,10 @@ snapshots:
dependencies:
ms: 2.0.0
+ debug@4.3.7:
+ dependencies:
+ ms: 2.1.3
+
debug@4.3.7(supports-color@9.4.0):
dependencies:
ms: 2.1.3
@@ -6597,7 +6611,7 @@ snapshots:
dependencies:
domelementtype: 2.3.0
- dompurify@3.2.1:
+ dompurify@3.2.3:
optionalDependencies:
'@types/trusted-types': 2.0.7
@@ -7118,8 +7132,8 @@ snapshots:
http-proxy-agent@7.0.2:
dependencies:
- agent-base: 7.1.0(supports-color@9.4.0)
- debug: 4.3.7(supports-color@9.4.0)
+ agent-base: 7.1.0
+ debug: 4.3.7
transitivePeerDependencies:
- supports-color
@@ -7142,7 +7156,7 @@ snapshots:
https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.3
- debug: 4.3.7(supports-color@9.4.0)
+ debug: 4.3.7
transitivePeerDependencies:
- supports-color
@@ -7289,6 +7303,16 @@ snapshots:
isexe@2.0.0: {}
+ isomorphic-dompurify@2.20.0:
+ dependencies:
+ dompurify: 3.2.3
+ jsdom: 26.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - supports-color
+ - utf-8-validate
+
istanbul-lib-coverage@3.2.2: {}
istanbul-lib-report@3.0.1:
@@ -7300,7 +7324,7 @@ snapshots:
istanbul-lib-source-maps@5.0.6:
dependencies:
'@jridgewell/trace-mapping': 0.3.25
- debug: 4.3.7(supports-color@9.4.0)
+ debug: 4.3.7
istanbul-lib-coverage: 3.2.2
transitivePeerDependencies:
- supports-color
@@ -8871,7 +8895,7 @@ snapshots:
vite-node@2.1.8(@types/node@22.7.4)(terser@5.26.0):
dependencies:
cac: 6.7.14
- debug: 4.3.7(supports-color@9.4.0)
+ debug: 4.3.7
es-module-lexer: 1.5.4
pathe: 1.1.2
vite: 5.4.11(@types/node@22.7.4)(terser@5.26.0)
@@ -8974,7 +8998,7 @@ snapshots:
'@vitest/spy': 2.1.8
'@vitest/utils': 2.1.8
chai: 5.1.2
- debug: 4.3.7(supports-color@9.4.0)
+ debug: 4.3.7
expect-type: 1.1.0
magic-string: 0.30.13
pathe: 1.1.2