vue3-markdown-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/NOTICE.md +16 -0
- package/README.md +177 -0
- package/SECURITY.md +14 -0
- package/lib/core.cjs +197 -0
- package/lib/core.cjs.map +7 -0
- package/lib/core.js +165 -0
- package/lib/core.js.map +7 -0
- package/lib/index.cjs +363 -0
- package/lib/index.cjs.map +7 -0
- package/lib/index.js +331 -0
- package/lib/index.js.map +7 -0
- package/lib/preview.cjs +325 -0
- package/lib/preview.cjs.map +7 -0
- package/lib/preview.js +295 -0
- package/lib/preview.js.map +7 -0
- package/lib/style/preview.css +23 -0
- package/lib/theme/github.cjs +143 -0
- package/lib/theme/github.cjs.map +7 -0
- package/lib/theme/github.js +111 -0
- package/lib/theme/github.js.map +7 -0
- package/lib/theme/style/github.css +187 -0
- package/package.json +112 -0
- package/types/core.d.ts +40 -0
- package/types/index.d.ts +20 -0
- package/types/preview.d.ts +39 -0
- package/types/theme-github.d.ts +15 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Add a Vue 3.5 Markdown preview component using the Composition API.
|
|
6
|
+
- Preserve the Vue 2 package's preview props, events, theme API, renderer, and sanitizer behavior.
|
|
7
|
+
- Add Vue 3 application-plugin installation and component-mount tests.
|
|
8
|
+
- Add ESM, CommonJS, TypeScript declaration, modern, and compatibility entry points.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 eatmeatHJ
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
Vue 3 Markdown Kit is distributed under the MIT License.
|
|
4
|
+
|
|
5
|
+
Its runtime dependencies are used through their public APIs and are not copied
|
|
6
|
+
into this repository:
|
|
7
|
+
|
|
8
|
+
- DOMPurify — Mozilla Public License 2.0 or Apache License 2.0
|
|
9
|
+
- highlight.js — BSD 3-Clause License
|
|
10
|
+
- markdown-it — MIT License
|
|
11
|
+
- markdown-it-attrs — MIT License
|
|
12
|
+
- Vue.js — MIT License (peer dependency)
|
|
13
|
+
|
|
14
|
+
The generated JavaScript bundles keep runtime dependencies external. Consumers
|
|
15
|
+
should include the dependency licenses in their own software notices as
|
|
16
|
+
required by their distribution process.
|
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Vue 3 Markdown Kit
|
|
2
|
+
|
|
3
|
+
A small, auditable Markdown preview toolkit for Vue 3.5. It provides a
|
|
4
|
+
component-friendly preview API, a framework-independent renderer, and
|
|
5
|
+
GitHub-style presentation.
|
|
6
|
+
|
|
7
|
+
This package is the Vue 3 counterpart of
|
|
8
|
+
[`vue-markdown-kit`](https://github.com/eatmeatHJ/vue-markdown-kit). It keeps
|
|
9
|
+
the same preview props, events, themes, CSS, and import layout while using the
|
|
10
|
+
Vue 3 application and rendering APIs.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- Vue 3.5 preview component built with the Composition API
|
|
15
|
+
- Markdown rendering through markdown-it
|
|
16
|
+
- HTML sanitization through DOMPurify
|
|
17
|
+
- Opt-in, tree-shakeable highlight.js language registration
|
|
18
|
+
- GitHub-style Markdown CSS
|
|
19
|
+
- Image-click and rendered-content events
|
|
20
|
+
- TypeScript declarations and ESM/CommonJS builds
|
|
21
|
+
- Compatibility and modern package entry points
|
|
22
|
+
|
|
23
|
+
## Requirements
|
|
24
|
+
|
|
25
|
+
- Vue 3.5 or newer
|
|
26
|
+
- Node.js 18 or newer for package tooling
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npm install vue3-markdown-kit
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Recommended usage
|
|
35
|
+
|
|
36
|
+
Configure the theme once in your application entry point:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { createApp } from 'vue'
|
|
40
|
+
import App from './App.vue'
|
|
41
|
+
|
|
42
|
+
import VueMarkdownKit, { VMdPreview, githubTheme } from 'vue3-markdown-kit'
|
|
43
|
+
import 'vue3-markdown-kit/style/preview.css'
|
|
44
|
+
import 'vue3-markdown-kit/theme/style/github.css'
|
|
45
|
+
|
|
46
|
+
VMdPreview.use(githubTheme)
|
|
47
|
+
|
|
48
|
+
createApp(App)
|
|
49
|
+
.use(VueMarkdownKit)
|
|
50
|
+
.mount('#app')
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The plugin globally registers `<v-md-preview>`:
|
|
54
|
+
|
|
55
|
+
```vue
|
|
56
|
+
<script setup lang="ts">
|
|
57
|
+
import { ref } from 'vue'
|
|
58
|
+
|
|
59
|
+
const markdown = ref('# Hello, Vue 3')
|
|
60
|
+
|
|
61
|
+
function handleImageClick(images: string[], index: number) {
|
|
62
|
+
console.log(images[index])
|
|
63
|
+
}
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<template>
|
|
67
|
+
<v-md-preview
|
|
68
|
+
:text="markdown"
|
|
69
|
+
@image-click="handleImageClick"
|
|
70
|
+
/>
|
|
71
|
+
</template>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
For local registration, import the component directly:
|
|
75
|
+
|
|
76
|
+
```vue
|
|
77
|
+
<script setup lang="ts">
|
|
78
|
+
import { VMdPreview, githubTheme } from 'vue3-markdown-kit'
|
|
79
|
+
import 'vue3-markdown-kit/style/preview.css'
|
|
80
|
+
import 'vue3-markdown-kit/theme/style/github.css'
|
|
81
|
+
|
|
82
|
+
VMdPreview.use(githubTheme)
|
|
83
|
+
|
|
84
|
+
const markdown = '# Locally registered component'
|
|
85
|
+
</script>
|
|
86
|
+
|
|
87
|
+
<template>
|
|
88
|
+
<VMdPreview :text="markdown" />
|
|
89
|
+
</template>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## highlight.js
|
|
93
|
+
|
|
94
|
+
Register only the languages your application uses:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { VMdPreview, githubTheme } from 'vue3-markdown-kit'
|
|
98
|
+
import hljs from 'highlight.js/lib/core'
|
|
99
|
+
import javascript from 'highlight.js/lib/languages/javascript'
|
|
100
|
+
|
|
101
|
+
hljs.registerLanguage('javascript', javascript)
|
|
102
|
+
|
|
103
|
+
VMdPreview.use(githubTheme, {
|
|
104
|
+
Hljs: hljs,
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Compatibility entry points
|
|
109
|
+
|
|
110
|
+
Integrations migrating from the Vue 2 package can retain the old import
|
|
111
|
+
layout and change only the package name:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import VMdPreview from 'vue3-markdown-kit/lib/preview'
|
|
115
|
+
import 'vue3-markdown-kit/lib/style/preview.css'
|
|
116
|
+
|
|
117
|
+
import githubTheme from 'vue3-markdown-kit/lib/theme/github.js'
|
|
118
|
+
import 'vue3-markdown-kit/lib/theme/style/github.css'
|
|
119
|
+
|
|
120
|
+
VMdPreview.use(githubTheme)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Vue 3 uses `createApp(...).use(...)`; replace any Vue 2 `Vue.use(...)`,
|
|
124
|
+
`Vue.extend(...)`, `$mount()`, `$on()`, and `$destroy()` integration code with
|
|
125
|
+
their Vue 3 equivalents.
|
|
126
|
+
|
|
127
|
+
## Framework-independent renderer
|
|
128
|
+
|
|
129
|
+
The renderer and sanitizer can be used without mounting a Vue component:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import {
|
|
133
|
+
createMarkdownRenderer,
|
|
134
|
+
sanitizeHtml,
|
|
135
|
+
} from 'vue3-markdown-kit/core'
|
|
136
|
+
|
|
137
|
+
const parser = createMarkdownRenderer()
|
|
138
|
+
const safeHtml = sanitizeHtml(parser.render('# Hello'))
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Preview props
|
|
142
|
+
|
|
143
|
+
| Prop | Type | Default | Description |
|
|
144
|
+
| --- | --- | --- | --- |
|
|
145
|
+
| `text` | `string` | `''` | Markdown source |
|
|
146
|
+
| `theme` | `object` | `undefined` | Per-instance theme plugin or theme config |
|
|
147
|
+
| `tabSize` | `number` | `2` | CSS tab size |
|
|
148
|
+
| `scrollContainer` | `function` | `() => window` | Scroll target retained for API compatibility |
|
|
149
|
+
| `top` | `number` | `0` | Scroll offset retained for API compatibility |
|
|
150
|
+
| `sanitizeOptions` | `object` | `{}` | Additional DOMPurify options |
|
|
151
|
+
|
|
152
|
+
## Events
|
|
153
|
+
|
|
154
|
+
- `change(text, html)` fires after Markdown is rendered and sanitized.
|
|
155
|
+
- `image-click(images, index)` fires when a rendered image is clicked.
|
|
156
|
+
|
|
157
|
+
## Security
|
|
158
|
+
|
|
159
|
+
Raw HTML is enabled for compatibility, then sanitized as the final rendering
|
|
160
|
+
step. Applications that do not need raw HTML can pass `html: false` when
|
|
161
|
+
creating a custom renderer. Keep all runtime dependencies updated and apply
|
|
162
|
+
an application-level Markdown input length limit.
|
|
163
|
+
|
|
164
|
+
## Development
|
|
165
|
+
|
|
166
|
+
```sh
|
|
167
|
+
npm ci
|
|
168
|
+
npm test
|
|
169
|
+
npm run check
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The generated `lib` directory is committed so the package can also be
|
|
173
|
+
installed directly from GitHub.
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT. See [LICENSE](LICENSE) and [NOTICE.md](NOTICE.md).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Only the latest released minor version receives security updates.
|
|
6
|
+
|
|
7
|
+
## Reporting a vulnerability
|
|
8
|
+
|
|
9
|
+
Please report suspected vulnerabilities privately through GitHub's private
|
|
10
|
+
vulnerability reporting feature. Do not include exploit details in a public
|
|
11
|
+
issue before a fix is available.
|
|
12
|
+
|
|
13
|
+
Markdown rendering is performed in the browser. Rendered HTML is sanitized by
|
|
14
|
+
DOMPurify before it reaches Vue's `innerHTML` render property.
|
package/lib/core.cjs
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/core/index.js
|
|
30
|
+
var core_exports = {};
|
|
31
|
+
__export(core_exports, {
|
|
32
|
+
createMarkdownRenderer: () => createMarkdownRenderer,
|
|
33
|
+
sanitizeHtml: () => sanitizeHtml,
|
|
34
|
+
sanitizer: () => sanitizer,
|
|
35
|
+
slugifyHeading: () => slugifyHeading
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(core_exports);
|
|
38
|
+
|
|
39
|
+
// src/core/renderer.js
|
|
40
|
+
var import_markdown_it = __toESM(require("markdown-it"), 1);
|
|
41
|
+
var import_markdown_it_attrs = __toESM(require("markdown-it-attrs"), 1);
|
|
42
|
+
function escapeHtml(value) {
|
|
43
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
44
|
+
}
|
|
45
|
+
function slugifyHeading(value) {
|
|
46
|
+
const slug = String(value || "").normalize("NFKC").trim().toLowerCase().replace(/\s+/g, "-").replace(/[^\p{Letter}\p{Number}_-]+/gu, "").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
|
|
47
|
+
return slug || "section";
|
|
48
|
+
}
|
|
49
|
+
function highlightCode(code, language, options) {
|
|
50
|
+
const {
|
|
51
|
+
Hljs,
|
|
52
|
+
codeHighlightExtensionMap = {},
|
|
53
|
+
codeBlockClass = (lang) => `v-md-hljs-${lang || ""}`
|
|
54
|
+
} = options;
|
|
55
|
+
const normalizedLanguage = codeHighlightExtensionMap[language] || language;
|
|
56
|
+
let highlighted = escapeHtml(code);
|
|
57
|
+
if (Hljs && normalizedLanguage && typeof Hljs.getLanguage === "function" && Hljs.getLanguage(normalizedLanguage)) {
|
|
58
|
+
try {
|
|
59
|
+
highlighted = Hljs.highlight(code, {
|
|
60
|
+
language: normalizedLanguage,
|
|
61
|
+
ignoreIllegals: true
|
|
62
|
+
}).value;
|
|
63
|
+
} catch {
|
|
64
|
+
highlighted = Hljs.highlight(normalizedLanguage, code, true).value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return `<pre class="${escapeHtml(codeBlockClass(normalizedLanguage))}"><code>${highlighted}</code></pre>`;
|
|
68
|
+
}
|
|
69
|
+
function installHeadingAnchors(parser) {
|
|
70
|
+
parser.core.ruler.before("block", "vue_markdown_kit_heading_state", (state) => {
|
|
71
|
+
state.env.__vueMarkdownKitHeadingSlugs = /* @__PURE__ */ new Map();
|
|
72
|
+
});
|
|
73
|
+
const fallback = parser.renderer.rules.heading_open || ((tokens, index, options, _env, self) => self.renderToken(tokens, index, options));
|
|
74
|
+
parser.renderer.rules.heading_open = (tokens, index, options, env, self) => {
|
|
75
|
+
const inline = tokens[index + 1];
|
|
76
|
+
const baseSlug = slugifyHeading(inline && inline.content);
|
|
77
|
+
const slugs = env.__vueMarkdownKitHeadingSlugs || /* @__PURE__ */ new Map();
|
|
78
|
+
const count = slugs.get(baseSlug) || 0;
|
|
79
|
+
const slug = count === 0 ? baseSlug : `${baseSlug}-${count}`;
|
|
80
|
+
slugs.set(baseSlug, count + 1);
|
|
81
|
+
env.__vueMarkdownKitHeadingSlugs = slugs;
|
|
82
|
+
tokens[index].attrSet("id", slug);
|
|
83
|
+
tokens[index].attrSet("data-v-md-heading", slug);
|
|
84
|
+
return fallback(tokens, index, options, env, self);
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function installSafeExternalLinks(parser) {
|
|
88
|
+
const fallback = parser.renderer.rules.link_open || ((tokens, index, options, _env, self) => self.renderToken(tokens, index, options));
|
|
89
|
+
parser.renderer.rules.link_open = (tokens, index, options, env, self) => {
|
|
90
|
+
const token = tokens[index];
|
|
91
|
+
const href = token.attrGet("href") || "";
|
|
92
|
+
if (/^https?:\/\//i.test(href)) {
|
|
93
|
+
token.attrSet("target", "_blank");
|
|
94
|
+
token.attrSet("rel", "noopener noreferrer");
|
|
95
|
+
}
|
|
96
|
+
return fallback(tokens, index, options, env, self);
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function createMarkdownRenderer(options = {}) {
|
|
100
|
+
var _a, _b, _c, _d;
|
|
101
|
+
const parser = new import_markdown_it.default({
|
|
102
|
+
html: (_a = options.html) != null ? _a : true,
|
|
103
|
+
breaks: (_b = options.breaks) != null ? _b : true,
|
|
104
|
+
linkify: (_c = options.linkify) != null ? _c : false,
|
|
105
|
+
typographer: (_d = options.typographer) != null ? _d : true,
|
|
106
|
+
highlight: (code, language) => highlightCode(code, language, options)
|
|
107
|
+
});
|
|
108
|
+
if (options.attrs !== false) {
|
|
109
|
+
parser.use(import_markdown_it_attrs.default, {
|
|
110
|
+
leftDelimiter: "{{{",
|
|
111
|
+
rightDelimiter: "}}}",
|
|
112
|
+
allowedAttributes: [
|
|
113
|
+
"class",
|
|
114
|
+
"height",
|
|
115
|
+
"id",
|
|
116
|
+
"title",
|
|
117
|
+
"width",
|
|
118
|
+
...options.allowedAttributes || []
|
|
119
|
+
]
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
installHeadingAnchors(parser);
|
|
123
|
+
installSafeExternalLinks(parser);
|
|
124
|
+
return parser;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/core/sanitize.js
|
|
128
|
+
var import_dompurify = __toESM(require("dompurify"), 1);
|
|
129
|
+
var BASE_SANITIZE_OPTIONS = Object.freeze({
|
|
130
|
+
USE_PROFILES: {
|
|
131
|
+
html: true
|
|
132
|
+
},
|
|
133
|
+
ADD_ATTR: [
|
|
134
|
+
"data-mention-id",
|
|
135
|
+
"data-v-md-anchor",
|
|
136
|
+
"data-v-md-heading",
|
|
137
|
+
"data-v-md-line",
|
|
138
|
+
"rel",
|
|
139
|
+
"target"
|
|
140
|
+
],
|
|
141
|
+
FORBID_TAGS: ["script"]
|
|
142
|
+
});
|
|
143
|
+
var sharedOptions = {};
|
|
144
|
+
function mergeOptions(base, extra) {
|
|
145
|
+
const merged = {
|
|
146
|
+
...base,
|
|
147
|
+
...extra
|
|
148
|
+
};
|
|
149
|
+
for (const key of ["ADD_ATTR", "ADD_TAGS", "FORBID_ATTR", "FORBID_TAGS"]) {
|
|
150
|
+
if (base[key] || extra[key]) {
|
|
151
|
+
merged[key] = [.../* @__PURE__ */ new Set([...base[key] || [], ...extra[key] || []])];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (base.USE_PROFILES || extra.USE_PROFILES) {
|
|
155
|
+
merged.USE_PROFILES = {
|
|
156
|
+
...base.USE_PROFILES || {},
|
|
157
|
+
...extra.USE_PROFILES || {}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return merged;
|
|
161
|
+
}
|
|
162
|
+
function resolvePurifier(windowLike) {
|
|
163
|
+
if (typeof import_dompurify.default.sanitize === "function") {
|
|
164
|
+
return import_dompurify.default;
|
|
165
|
+
}
|
|
166
|
+
if (windowLike && typeof import_dompurify.default === "function") {
|
|
167
|
+
return (0, import_dompurify.default)(windowLike);
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
function escapeHtml2(value) {
|
|
172
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
173
|
+
}
|
|
174
|
+
function sanitizeHtml(dirtyHtml, options = {}, windowLike = typeof window === "undefined" ? void 0 : window) {
|
|
175
|
+
const purifier = resolvePurifier(windowLike);
|
|
176
|
+
if (!purifier || typeof purifier.sanitize !== "function") {
|
|
177
|
+
return escapeHtml2(dirtyHtml || "");
|
|
178
|
+
}
|
|
179
|
+
return purifier.sanitize(
|
|
180
|
+
dirtyHtml || "",
|
|
181
|
+
mergeOptions(mergeOptions(BASE_SANITIZE_OPTIONS, sharedOptions), options)
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
var sanitizer = {
|
|
185
|
+
process(dirtyHtml, options) {
|
|
186
|
+
return sanitizeHtml(dirtyHtml, options);
|
|
187
|
+
},
|
|
188
|
+
extend(options = {}) {
|
|
189
|
+
sharedOptions = mergeOptions(sharedOptions, options);
|
|
190
|
+
return this;
|
|
191
|
+
},
|
|
192
|
+
reset() {
|
|
193
|
+
sharedOptions = {};
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
//# sourceMappingURL=core.cjs.map
|
package/lib/core.cjs.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/index.js", "../src/core/renderer.js", "../src/core/sanitize.js"],
|
|
4
|
+
"sourcesContent": ["export { createMarkdownRenderer, slugifyHeading } from './renderer.js'\nexport { sanitizeHtml, sanitizer } from './sanitize.js'\n", "import MarkdownIt from 'markdown-it'\nimport markdownItAttrs from 'markdown-it-attrs'\n\nfunction escapeHtml(value) {\n return String(value)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\nexport function slugifyHeading(value) {\n const slug = String(value || '')\n .normalize('NFKC')\n .trim()\n .toLowerCase()\n .replace(/\\s+/g, '-')\n .replace(/[^\\p{Letter}\\p{Number}_-]+/gu, '')\n .replace(/-{2,}/g, '-')\n .replace(/^-|-$/g, '')\n\n return slug || 'section'\n}\n\nfunction highlightCode(code, language, options) {\n const {\n Hljs,\n codeHighlightExtensionMap = {},\n codeBlockClass = (lang) => `v-md-hljs-${lang || ''}`,\n } = options\n\n const normalizedLanguage = codeHighlightExtensionMap[language] || language\n let highlighted = escapeHtml(code)\n\n if (\n Hljs &&\n normalizedLanguage &&\n typeof Hljs.getLanguage === 'function' &&\n Hljs.getLanguage(normalizedLanguage)\n ) {\n try {\n highlighted = Hljs.highlight(code, {\n language: normalizedLanguage,\n ignoreIllegals: true,\n }).value\n } catch {\n // highlight.js 10 compatibility.\n highlighted = Hljs.highlight(normalizedLanguage, code, true).value\n }\n }\n\n return `<pre class=\"${escapeHtml(codeBlockClass(normalizedLanguage))}\"><code>${highlighted}</code></pre>`\n}\n\nfunction installHeadingAnchors(parser) {\n parser.core.ruler.before('block', 'vue_markdown_kit_heading_state', (state) => {\n state.env.__vueMarkdownKitHeadingSlugs = new Map()\n })\n\n const fallback =\n parser.renderer.rules.heading_open ||\n ((tokens, index, options, _env, self) => self.renderToken(tokens, index, options))\n\n parser.renderer.rules.heading_open = (tokens, index, options, env, self) => {\n const inline = tokens[index + 1]\n const baseSlug = slugifyHeading(inline && inline.content)\n const slugs = env.__vueMarkdownKitHeadingSlugs || new Map()\n const count = slugs.get(baseSlug) || 0\n const slug = count === 0 ? baseSlug : `${baseSlug}-${count}`\n\n slugs.set(baseSlug, count + 1)\n env.__vueMarkdownKitHeadingSlugs = slugs\n\n tokens[index].attrSet('id', slug)\n tokens[index].attrSet('data-v-md-heading', slug)\n\n return fallback(tokens, index, options, env, self)\n }\n}\n\nfunction installSafeExternalLinks(parser) {\n const fallback =\n parser.renderer.rules.link_open ||\n ((tokens, index, options, _env, self) => self.renderToken(tokens, index, options))\n\n parser.renderer.rules.link_open = (tokens, index, options, env, self) => {\n const token = tokens[index]\n const href = token.attrGet('href') || ''\n\n if (/^https?:\\/\\//i.test(href)) {\n token.attrSet('target', '_blank')\n token.attrSet('rel', 'noopener noreferrer')\n }\n\n return fallback(tokens, index, options, env, self)\n }\n}\n\nexport function createMarkdownRenderer(options = {}) {\n const parser = new MarkdownIt({\n html: options.html ?? true,\n breaks: options.breaks ?? true,\n linkify: options.linkify ?? false,\n typographer: options.typographer ?? true,\n highlight: (code, language) => highlightCode(code, language, options),\n })\n\n if (options.attrs !== false) {\n parser.use(markdownItAttrs, {\n leftDelimiter: '{{{',\n rightDelimiter: '}}}',\n allowedAttributes: [\n 'class',\n 'height',\n 'id',\n 'title',\n 'width',\n ...(options.allowedAttributes || []),\n ],\n })\n }\n\n installHeadingAnchors(parser)\n installSafeExternalLinks(parser)\n\n return parser\n}\n", "import createDOMPurify from 'dompurify'\n\nconst BASE_SANITIZE_OPTIONS = Object.freeze({\n USE_PROFILES: {\n html: true,\n },\n ADD_ATTR: [\n 'data-mention-id',\n 'data-v-md-anchor',\n 'data-v-md-heading',\n 'data-v-md-line',\n 'rel',\n 'target',\n ],\n FORBID_TAGS: ['script'],\n})\n\nlet sharedOptions = {}\n\nfunction mergeOptions(base, extra) {\n const merged = {\n ...base,\n ...extra,\n }\n\n for (const key of ['ADD_ATTR', 'ADD_TAGS', 'FORBID_ATTR', 'FORBID_TAGS']) {\n if (base[key] || extra[key]) {\n merged[key] = [...new Set([...(base[key] || []), ...(extra[key] || [])])]\n }\n }\n\n if (base.USE_PROFILES || extra.USE_PROFILES) {\n merged.USE_PROFILES = {\n ...(base.USE_PROFILES || {}),\n ...(extra.USE_PROFILES || {}),\n }\n }\n\n return merged\n}\n\nfunction resolvePurifier(windowLike) {\n if (typeof createDOMPurify.sanitize === 'function') {\n return createDOMPurify\n }\n\n if (windowLike && typeof createDOMPurify === 'function') {\n return createDOMPurify(windowLike)\n }\n\n return null\n}\n\nfunction escapeHtml(value) {\n return String(value)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n}\n\nexport function sanitizeHtml(\n dirtyHtml,\n options = {},\n windowLike = typeof window === 'undefined' ? undefined : window,\n) {\n const purifier = resolvePurifier(windowLike)\n\n // Fail closed in non-DOM environments rather than returning executable HTML.\n if (!purifier || typeof purifier.sanitize !== 'function') {\n return escapeHtml(dirtyHtml || '')\n }\n\n return purifier.sanitize(\n dirtyHtml || '',\n mergeOptions(mergeOptions(BASE_SANITIZE_OPTIONS, sharedOptions), options),\n )\n}\n\nexport const sanitizer = {\n process(dirtyHtml, options) {\n return sanitizeHtml(dirtyHtml, options)\n },\n\n extend(options = {}) {\n sharedOptions = mergeOptions(sharedOptions, options)\n return this\n },\n\n reset() {\n sharedOptions = {}\n return this\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["MarkdownIt", "markdownItAttrs", "createDOMPurify", "escapeHtml"]
|
|
7
|
+
}
|
package/lib/core.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// src/core/renderer.js
|
|
2
|
+
import MarkdownIt from "markdown-it";
|
|
3
|
+
import markdownItAttrs from "markdown-it-attrs";
|
|
4
|
+
function escapeHtml(value) {
|
|
5
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
6
|
+
}
|
|
7
|
+
function slugifyHeading(value) {
|
|
8
|
+
const slug = String(value || "").normalize("NFKC").trim().toLowerCase().replace(/\s+/g, "-").replace(/[^\p{Letter}\p{Number}_-]+/gu, "").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
|
|
9
|
+
return slug || "section";
|
|
10
|
+
}
|
|
11
|
+
function highlightCode(code, language, options) {
|
|
12
|
+
const {
|
|
13
|
+
Hljs,
|
|
14
|
+
codeHighlightExtensionMap = {},
|
|
15
|
+
codeBlockClass = (lang) => `v-md-hljs-${lang || ""}`
|
|
16
|
+
} = options;
|
|
17
|
+
const normalizedLanguage = codeHighlightExtensionMap[language] || language;
|
|
18
|
+
let highlighted = escapeHtml(code);
|
|
19
|
+
if (Hljs && normalizedLanguage && typeof Hljs.getLanguage === "function" && Hljs.getLanguage(normalizedLanguage)) {
|
|
20
|
+
try {
|
|
21
|
+
highlighted = Hljs.highlight(code, {
|
|
22
|
+
language: normalizedLanguage,
|
|
23
|
+
ignoreIllegals: true
|
|
24
|
+
}).value;
|
|
25
|
+
} catch {
|
|
26
|
+
highlighted = Hljs.highlight(normalizedLanguage, code, true).value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return `<pre class="${escapeHtml(codeBlockClass(normalizedLanguage))}"><code>${highlighted}</code></pre>`;
|
|
30
|
+
}
|
|
31
|
+
function installHeadingAnchors(parser) {
|
|
32
|
+
parser.core.ruler.before("block", "vue_markdown_kit_heading_state", (state) => {
|
|
33
|
+
state.env.__vueMarkdownKitHeadingSlugs = /* @__PURE__ */ new Map();
|
|
34
|
+
});
|
|
35
|
+
const fallback = parser.renderer.rules.heading_open || ((tokens, index, options, _env, self) => self.renderToken(tokens, index, options));
|
|
36
|
+
parser.renderer.rules.heading_open = (tokens, index, options, env, self) => {
|
|
37
|
+
const inline = tokens[index + 1];
|
|
38
|
+
const baseSlug = slugifyHeading(inline && inline.content);
|
|
39
|
+
const slugs = env.__vueMarkdownKitHeadingSlugs || /* @__PURE__ */ new Map();
|
|
40
|
+
const count = slugs.get(baseSlug) || 0;
|
|
41
|
+
const slug = count === 0 ? baseSlug : `${baseSlug}-${count}`;
|
|
42
|
+
slugs.set(baseSlug, count + 1);
|
|
43
|
+
env.__vueMarkdownKitHeadingSlugs = slugs;
|
|
44
|
+
tokens[index].attrSet("id", slug);
|
|
45
|
+
tokens[index].attrSet("data-v-md-heading", slug);
|
|
46
|
+
return fallback(tokens, index, options, env, self);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function installSafeExternalLinks(parser) {
|
|
50
|
+
const fallback = parser.renderer.rules.link_open || ((tokens, index, options, _env, self) => self.renderToken(tokens, index, options));
|
|
51
|
+
parser.renderer.rules.link_open = (tokens, index, options, env, self) => {
|
|
52
|
+
const token = tokens[index];
|
|
53
|
+
const href = token.attrGet("href") || "";
|
|
54
|
+
if (/^https?:\/\//i.test(href)) {
|
|
55
|
+
token.attrSet("target", "_blank");
|
|
56
|
+
token.attrSet("rel", "noopener noreferrer");
|
|
57
|
+
}
|
|
58
|
+
return fallback(tokens, index, options, env, self);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function createMarkdownRenderer(options = {}) {
|
|
62
|
+
var _a, _b, _c, _d;
|
|
63
|
+
const parser = new MarkdownIt({
|
|
64
|
+
html: (_a = options.html) != null ? _a : true,
|
|
65
|
+
breaks: (_b = options.breaks) != null ? _b : true,
|
|
66
|
+
linkify: (_c = options.linkify) != null ? _c : false,
|
|
67
|
+
typographer: (_d = options.typographer) != null ? _d : true,
|
|
68
|
+
highlight: (code, language) => highlightCode(code, language, options)
|
|
69
|
+
});
|
|
70
|
+
if (options.attrs !== false) {
|
|
71
|
+
parser.use(markdownItAttrs, {
|
|
72
|
+
leftDelimiter: "{{{",
|
|
73
|
+
rightDelimiter: "}}}",
|
|
74
|
+
allowedAttributes: [
|
|
75
|
+
"class",
|
|
76
|
+
"height",
|
|
77
|
+
"id",
|
|
78
|
+
"title",
|
|
79
|
+
"width",
|
|
80
|
+
...options.allowedAttributes || []
|
|
81
|
+
]
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
installHeadingAnchors(parser);
|
|
85
|
+
installSafeExternalLinks(parser);
|
|
86
|
+
return parser;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/core/sanitize.js
|
|
90
|
+
import createDOMPurify from "dompurify";
|
|
91
|
+
var BASE_SANITIZE_OPTIONS = Object.freeze({
|
|
92
|
+
USE_PROFILES: {
|
|
93
|
+
html: true
|
|
94
|
+
},
|
|
95
|
+
ADD_ATTR: [
|
|
96
|
+
"data-mention-id",
|
|
97
|
+
"data-v-md-anchor",
|
|
98
|
+
"data-v-md-heading",
|
|
99
|
+
"data-v-md-line",
|
|
100
|
+
"rel",
|
|
101
|
+
"target"
|
|
102
|
+
],
|
|
103
|
+
FORBID_TAGS: ["script"]
|
|
104
|
+
});
|
|
105
|
+
var sharedOptions = {};
|
|
106
|
+
function mergeOptions(base, extra) {
|
|
107
|
+
const merged = {
|
|
108
|
+
...base,
|
|
109
|
+
...extra
|
|
110
|
+
};
|
|
111
|
+
for (const key of ["ADD_ATTR", "ADD_TAGS", "FORBID_ATTR", "FORBID_TAGS"]) {
|
|
112
|
+
if (base[key] || extra[key]) {
|
|
113
|
+
merged[key] = [.../* @__PURE__ */ new Set([...base[key] || [], ...extra[key] || []])];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (base.USE_PROFILES || extra.USE_PROFILES) {
|
|
117
|
+
merged.USE_PROFILES = {
|
|
118
|
+
...base.USE_PROFILES || {},
|
|
119
|
+
...extra.USE_PROFILES || {}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return merged;
|
|
123
|
+
}
|
|
124
|
+
function resolvePurifier(windowLike) {
|
|
125
|
+
if (typeof createDOMPurify.sanitize === "function") {
|
|
126
|
+
return createDOMPurify;
|
|
127
|
+
}
|
|
128
|
+
if (windowLike && typeof createDOMPurify === "function") {
|
|
129
|
+
return createDOMPurify(windowLike);
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
function escapeHtml2(value) {
|
|
134
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
135
|
+
}
|
|
136
|
+
function sanitizeHtml(dirtyHtml, options = {}, windowLike = typeof window === "undefined" ? void 0 : window) {
|
|
137
|
+
const purifier = resolvePurifier(windowLike);
|
|
138
|
+
if (!purifier || typeof purifier.sanitize !== "function") {
|
|
139
|
+
return escapeHtml2(dirtyHtml || "");
|
|
140
|
+
}
|
|
141
|
+
return purifier.sanitize(
|
|
142
|
+
dirtyHtml || "",
|
|
143
|
+
mergeOptions(mergeOptions(BASE_SANITIZE_OPTIONS, sharedOptions), options)
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
var sanitizer = {
|
|
147
|
+
process(dirtyHtml, options) {
|
|
148
|
+
return sanitizeHtml(dirtyHtml, options);
|
|
149
|
+
},
|
|
150
|
+
extend(options = {}) {
|
|
151
|
+
sharedOptions = mergeOptions(sharedOptions, options);
|
|
152
|
+
return this;
|
|
153
|
+
},
|
|
154
|
+
reset() {
|
|
155
|
+
sharedOptions = {};
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
export {
|
|
160
|
+
createMarkdownRenderer,
|
|
161
|
+
sanitizeHtml,
|
|
162
|
+
sanitizer,
|
|
163
|
+
slugifyHeading
|
|
164
|
+
};
|
|
165
|
+
//# sourceMappingURL=core.js.map
|
package/lib/core.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/renderer.js", "../src/core/sanitize.js"],
|
|
4
|
+
"sourcesContent": ["import MarkdownIt from 'markdown-it'\nimport markdownItAttrs from 'markdown-it-attrs'\n\nfunction escapeHtml(value) {\n return String(value)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\nexport function slugifyHeading(value) {\n const slug = String(value || '')\n .normalize('NFKC')\n .trim()\n .toLowerCase()\n .replace(/\\s+/g, '-')\n .replace(/[^\\p{Letter}\\p{Number}_-]+/gu, '')\n .replace(/-{2,}/g, '-')\n .replace(/^-|-$/g, '')\n\n return slug || 'section'\n}\n\nfunction highlightCode(code, language, options) {\n const {\n Hljs,\n codeHighlightExtensionMap = {},\n codeBlockClass = (lang) => `v-md-hljs-${lang || ''}`,\n } = options\n\n const normalizedLanguage = codeHighlightExtensionMap[language] || language\n let highlighted = escapeHtml(code)\n\n if (\n Hljs &&\n normalizedLanguage &&\n typeof Hljs.getLanguage === 'function' &&\n Hljs.getLanguage(normalizedLanguage)\n ) {\n try {\n highlighted = Hljs.highlight(code, {\n language: normalizedLanguage,\n ignoreIllegals: true,\n }).value\n } catch {\n // highlight.js 10 compatibility.\n highlighted = Hljs.highlight(normalizedLanguage, code, true).value\n }\n }\n\n return `<pre class=\"${escapeHtml(codeBlockClass(normalizedLanguage))}\"><code>${highlighted}</code></pre>`\n}\n\nfunction installHeadingAnchors(parser) {\n parser.core.ruler.before('block', 'vue_markdown_kit_heading_state', (state) => {\n state.env.__vueMarkdownKitHeadingSlugs = new Map()\n })\n\n const fallback =\n parser.renderer.rules.heading_open ||\n ((tokens, index, options, _env, self) => self.renderToken(tokens, index, options))\n\n parser.renderer.rules.heading_open = (tokens, index, options, env, self) => {\n const inline = tokens[index + 1]\n const baseSlug = slugifyHeading(inline && inline.content)\n const slugs = env.__vueMarkdownKitHeadingSlugs || new Map()\n const count = slugs.get(baseSlug) || 0\n const slug = count === 0 ? baseSlug : `${baseSlug}-${count}`\n\n slugs.set(baseSlug, count + 1)\n env.__vueMarkdownKitHeadingSlugs = slugs\n\n tokens[index].attrSet('id', slug)\n tokens[index].attrSet('data-v-md-heading', slug)\n\n return fallback(tokens, index, options, env, self)\n }\n}\n\nfunction installSafeExternalLinks(parser) {\n const fallback =\n parser.renderer.rules.link_open ||\n ((tokens, index, options, _env, self) => self.renderToken(tokens, index, options))\n\n parser.renderer.rules.link_open = (tokens, index, options, env, self) => {\n const token = tokens[index]\n const href = token.attrGet('href') || ''\n\n if (/^https?:\\/\\//i.test(href)) {\n token.attrSet('target', '_blank')\n token.attrSet('rel', 'noopener noreferrer')\n }\n\n return fallback(tokens, index, options, env, self)\n }\n}\n\nexport function createMarkdownRenderer(options = {}) {\n const parser = new MarkdownIt({\n html: options.html ?? true,\n breaks: options.breaks ?? true,\n linkify: options.linkify ?? false,\n typographer: options.typographer ?? true,\n highlight: (code, language) => highlightCode(code, language, options),\n })\n\n if (options.attrs !== false) {\n parser.use(markdownItAttrs, {\n leftDelimiter: '{{{',\n rightDelimiter: '}}}',\n allowedAttributes: [\n 'class',\n 'height',\n 'id',\n 'title',\n 'width',\n ...(options.allowedAttributes || []),\n ],\n })\n }\n\n installHeadingAnchors(parser)\n installSafeExternalLinks(parser)\n\n return parser\n}\n", "import createDOMPurify from 'dompurify'\n\nconst BASE_SANITIZE_OPTIONS = Object.freeze({\n USE_PROFILES: {\n html: true,\n },\n ADD_ATTR: [\n 'data-mention-id',\n 'data-v-md-anchor',\n 'data-v-md-heading',\n 'data-v-md-line',\n 'rel',\n 'target',\n ],\n FORBID_TAGS: ['script'],\n})\n\nlet sharedOptions = {}\n\nfunction mergeOptions(base, extra) {\n const merged = {\n ...base,\n ...extra,\n }\n\n for (const key of ['ADD_ATTR', 'ADD_TAGS', 'FORBID_ATTR', 'FORBID_TAGS']) {\n if (base[key] || extra[key]) {\n merged[key] = [...new Set([...(base[key] || []), ...(extra[key] || [])])]\n }\n }\n\n if (base.USE_PROFILES || extra.USE_PROFILES) {\n merged.USE_PROFILES = {\n ...(base.USE_PROFILES || {}),\n ...(extra.USE_PROFILES || {}),\n }\n }\n\n return merged\n}\n\nfunction resolvePurifier(windowLike) {\n if (typeof createDOMPurify.sanitize === 'function') {\n return createDOMPurify\n }\n\n if (windowLike && typeof createDOMPurify === 'function') {\n return createDOMPurify(windowLike)\n }\n\n return null\n}\n\nfunction escapeHtml(value) {\n return String(value)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n}\n\nexport function sanitizeHtml(\n dirtyHtml,\n options = {},\n windowLike = typeof window === 'undefined' ? undefined : window,\n) {\n const purifier = resolvePurifier(windowLike)\n\n // Fail closed in non-DOM environments rather than returning executable HTML.\n if (!purifier || typeof purifier.sanitize !== 'function') {\n return escapeHtml(dirtyHtml || '')\n }\n\n return purifier.sanitize(\n dirtyHtml || '',\n mergeOptions(mergeOptions(BASE_SANITIZE_OPTIONS, sharedOptions), options),\n )\n}\n\nexport const sanitizer = {\n process(dirtyHtml, options) {\n return sanitizeHtml(dirtyHtml, options)\n },\n\n extend(options = {}) {\n sharedOptions = mergeOptions(sharedOptions, options)\n return this\n },\n\n reset() {\n sharedOptions = {}\n return this\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["escapeHtml"]
|
|
7
|
+
}
|