vitepress-plugin-artalk 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 sugar
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/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # vitepress-plugin-artalk
2
+
3
+ [English](https://github.com/ATQQ/sugar-blog/blob/master/packages/vitepress-plugin-artalk/README-en.md) | 简体中文
4
+
5
+ VitePress 评论组件插件,集成了 [Artalk](https://artalk.js.org/) 评论系统,并包含了一个悬浮的评论跳转按钮(带移动端适配)。
6
+
7
+ ![示例](https://github.com/ATQQ/sugar-blog/blob/master/packages/vitepress-plugin-artalk/image.png?raw=true)
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ pnpm add vitepress-plugin-artalk artalk
13
+ ```
14
+
15
+ ## 使用
16
+
17
+ 在 `.vitepress/config.ts` 中引入插件:
18
+
19
+ ```ts
20
+ import { defineConfig } from 'vitepress'
21
+ import { artalkPlugin } from 'vitepress-plugin-artalk'
22
+
23
+ export default defineConfig({
24
+ vite: {
25
+ plugins: [
26
+ artalkPlugin({
27
+ site: 'site-name',
28
+ server: 'https://your-artalk-server.com',
29
+ // ...其他配置
30
+ })
31
+ ]
32
+ }
33
+ })
34
+ ```
35
+
36
+ ## Frontmatter 配置
37
+
38
+ 你也可以在单篇文章的 `frontmatter` 中动态覆盖或关闭评论配置:
39
+
40
+ ```yaml
41
+ ---
42
+ # 关闭评论
43
+ comment: false
44
+ ---
45
+
46
+ ---
47
+ # 覆盖评论配置
48
+ comment:
49
+ label: 热评
50
+ mobileMinify: false
51
+ ---
52
+ ```
53
+
54
+ ## 选项
55
+
56
+ 继承自 Artalk 的配置,同时包含以下插件独有配置:
57
+
58
+ | 参数 | 说明 | 类型 | 默认值 |
59
+ | --- | --- | --- | --- |
60
+ | site | 站点名称 | `string` | **必填** |
61
+ | server | Artalk 服务器地址 | `string` | **必填** |
62
+ | showCommentBtn | 是否显示右下角悬浮评论跳转按钮 | `boolean` | `true` |
63
+ | label | 悬浮按钮旁边的文字提示 | `string` | `'评论'` |
64
+ | mobileMinify | 移动端下是否隐藏文字提示仅显示图标 | `boolean` | `true` |
65
+ | icon | 自定义 SVG 图标代码 | `string` | - |
66
+
67
+ ## 类型定义
68
+
69
+ ```ts
70
+ export interface ArtalkPluginOptions {
71
+ site: string
72
+ server: string
73
+ mobileMinify?: boolean
74
+ label?: string
75
+ icon?: string
76
+ showCommentBtn?: boolean
77
+ [key: string]: any
78
+ }
79
+ ```
@@ -0,0 +1,187 @@
1
+ <script setup lang="ts">
2
+ import { useData, useRoute } from 'vitepress'
3
+ import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
4
+ import type Artalk from 'artalk'
5
+
6
+ // @ts-expect-error
7
+ import pluginOptions from 'virtual:artalk-plugin-options'
8
+ import { useElementSize, useElementVisibility, useWindowSize } from '@vueuse/core'
9
+ import Icon from './Icon.vue'
10
+
11
+ const { frontmatter, isDark, page } = useData()
12
+
13
+ const commentConfig = computed(() => {
14
+ // If explicitly disabled in frontmatter
15
+ if (frontmatter.value.comment === false) {
16
+ return false
17
+ }
18
+
19
+ // Merge frontmatter config if available, otherwise use plugin options
20
+ const fmConfig = frontmatter.value.comment
21
+ if (typeof fmConfig === 'object') {
22
+ return { ...pluginOptions, ...fmConfig }
23
+ }
24
+
25
+ return pluginOptions
26
+ })
27
+
28
+ const route = useRoute()
29
+ const artalk = ref<Artalk>()
30
+ const artalkContainer = ref<HTMLDivElement>()
31
+
32
+ function initArtalk() {
33
+ // CDN 异步加载,有优化空间
34
+ const observer = new MutationObserver((mutationsList, observer) => {
35
+ // @ts-expect-error
36
+ if (window.Artalk && commentConfig.value && artalkContainer.value) {
37
+ // @ts-expect-error
38
+ artalk.value = window.Artalk.init({
39
+ el: artalkContainer.value,
40
+ darkMode: isDark.value,
41
+ pageKey: route.path,
42
+ pageTitle: page.value.title,
43
+ server: commentConfig.value?.server,
44
+ site: commentConfig.value?.site,
45
+ ...commentConfig.value
46
+ })
47
+ observer.disconnect()
48
+ }
49
+ })
50
+
51
+ observer.observe(document.head, { subtree: true, childList: true, attributes: true, attributeFilter: ['id'] })
52
+ }
53
+
54
+ watch(() => route.path, () => {
55
+ if (artalk.value) {
56
+ artalk.value.update({
57
+ pageKey: route.path,
58
+ pageTitle: page.value.title,
59
+ })
60
+ artalk.value.reload()
61
+ }
62
+ })
63
+
64
+ onUnmounted(() => {
65
+ if (artalk.value) {
66
+ artalk.value.destroy()
67
+ }
68
+ })
69
+
70
+ watch(isDark, () => {
71
+ if (artalk.value) {
72
+ artalk.value.setDarkMode(isDark.value)
73
+ }
74
+ })
75
+
76
+ // Wrapper logic
77
+ const commentEl = ref(null)
78
+ const commentIsVisible = useElementVisibility(commentEl)
79
+
80
+ function handleScrollToComment() {
81
+ // @ts-expect-error
82
+ commentEl.value?.scrollIntoView({
83
+ behavior: 'smooth',
84
+ block: 'start'
85
+ })
86
+ }
87
+
88
+ const { width } = useWindowSize()
89
+ const mobileMinify = computed(() => width.value < 768 && (commentConfig.value?.mobileMinify ?? true))
90
+
91
+ const $vpDoc = typeof document !== 'undefined' ? document.querySelector('.vp-doc') || document.body : null
92
+ const el = ref<any>($vpDoc)
93
+ const { width: _docWidth } = useElementSize(el)
94
+ const docWidth = computed(() => `${_docWidth.value}px`)
95
+
96
+ onMounted(() => {
97
+ const vpDoc = document.querySelector('.vp-doc')
98
+ if (vpDoc) {
99
+ el.value = vpDoc
100
+ }
101
+ initArtalk()
102
+ })
103
+
104
+ const labelText = computed(() => {
105
+ return commentConfig.value?.label ?? '评论'
106
+ })
107
+
108
+ const showCommentBtn = computed(() => {
109
+ return commentConfig.value?.showCommentBtn ?? true
110
+ })
111
+ </script>
112
+
113
+ <template>
114
+ <div v-if="commentConfig" id="blog-comment-wrapper" ref="commentEl" class="blog-comment-wrapper" data-pagefind-ignore="all">
115
+ <div ref="artalkContainer" class="artalk-container" />
116
+ <div v-if="showCommentBtn && _docWidth" v-show="!commentIsVisible" class="comment-btn-wrapper">
117
+ <span v-if="!mobileMinify && labelText" class="icon-wrapper-text" @click="handleScrollToComment">
118
+ <Icon :size="20" :icon="commentConfig?.icon">
119
+ <svg data-v-f0aeb853="" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 1024 1024"><path fill="currentColor" d="M736 504a56 56 0 1 1 0-112 56 56 0 0 1 0 112m-224 0a56 56 0 1 1 0-112 56 56 0 0 1 0 112m-224 0a56 56 0 1 1 0-112 56 56 0 0 1 0 112M128 128v640h192v160l224-160h352V128z" /></svg>
120
+ </Icon>
121
+ <span class="text">
122
+ {{ labelText }}
123
+ </span>
124
+ </span>
125
+ <span v-else class="icon-wrapper" @click="handleScrollToComment">
126
+ <Icon :size="20" :icon="commentConfig?.icon">
127
+ <svg data-v-f0aeb853="" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 1024 1024"><path fill="currentColor" d="M736 504a56 56 0 1 1 0-112 56 56 0 0 1 0 112m-224 0a56 56 0 1 1 0-112 56 56 0 0 1 0 112M128 128v640h192v160l224-160h352V128z" /></svg>
128
+ </Icon>
129
+ </span>
130
+ </div>
131
+ </div>
132
+ </template>
133
+
134
+ <style scoped>
135
+ .artalk-container {
136
+ --at-color-main: var(--vp-c-brand-2);
137
+ }
138
+ .comment-btn-wrapper {
139
+ position: fixed;
140
+ width: v-bind(docWidth);
141
+ text-align: right;
142
+ bottom: 40px;
143
+ font-size: 16px;
144
+ transition: all 0.3s ease-in-out;
145
+ opacity: 0.6;
146
+ display: flex;
147
+ justify-content: right;
148
+ z-index: 200;
149
+ }
150
+ .comment-btn-wrapper:hover {
151
+ opacity: 1;
152
+ }
153
+ .comment-btn-wrapper .icon-wrapper,
154
+ .comment-btn-wrapper .icon-wrapper-text {
155
+ cursor: pointer;
156
+ border-radius: 50%;
157
+ position: relative;
158
+ right: -80px;
159
+ background-color: var(--vp-c-bg);
160
+ box-shadow: var(--box-shadow);
161
+ padding: 4px;
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: center;
165
+ background-color: var(--vp-c-brand-soft);
166
+ color: var(--vp-c-brand-1);
167
+ }
168
+ .comment-btn-wrapper .icon-wrapper:hover,
169
+ .comment-btn-wrapper .icon-wrapper-text:hover {
170
+ box-shadow: var(--box-shadow-hover);
171
+ }
172
+ .comment-btn-wrapper .icon-wrapper-text {
173
+ border-radius: 2px;
174
+ padding: 2px 6px;
175
+ }
176
+ .comment-btn-wrapper .icon-wrapper-text span.text {
177
+ font-size: 12px;
178
+ margin-left: 4px;
179
+ }
180
+
181
+ @media screen and (max-width: 1200px) {
182
+ .comment-btn-wrapper .icon-wrapper,
183
+ .comment-btn-wrapper .icon-wrapper-text {
184
+ position: static;
185
+ }
186
+ }
187
+ </style>
@@ -0,0 +1,33 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from 'vue'
3
+
4
+ const props = defineProps<{
5
+ size?: string | number
6
+ icon?: string
7
+ }>()
8
+
9
+ const size = computed(() => props.size && (typeof props.size === 'number' ? `${props.size}px` : props.size))
10
+ </script>
11
+
12
+ <template>
13
+ <i v-if="props.icon" class="sugar-theme-icon" :style="{ fontSize: size }" v-html="props.icon" />
14
+ <i v-else class="sugar-theme-icon" :style="{ fontSize: size }">
15
+ <slot />
16
+ </i>
17
+ </template>
18
+
19
+ <style lang="css" scoped>
20
+ .sugar-theme-icon {
21
+ --color: inherit;
22
+ align-items: center;
23
+ display: inline-flex;
24
+ height: 1em;
25
+ justify-content: center;
26
+ line-height: 1em;
27
+ position: relative;
28
+ width: 1em;
29
+ fill: currentColor;
30
+ color: var(--color);
31
+ font-size: inherit;
32
+ }
33
+ </style>
@@ -0,0 +1,15 @@
1
+ /* TODO:按实际情况添加不同模式的变量 */
2
+ html .demo {
3
+ --box-shadow: 0 1px 8px 0 rgba(0, 0, 0, 0.1);
4
+ }
5
+
6
+ html.dark .demo {
7
+ --box-shadow: 0 1px 8px 0 rgba(0, 0, 0, 0.6);
8
+ }
9
+
10
+ .parent-demo{
11
+ text-align: center;
12
+ }
13
+ .parent-demo h1{
14
+ color: red;
15
+ }
@@ -0,0 +1,3 @@
1
+ export declare function parseStringStyle(cssText: string): Record<string, string | number>;
2
+ export declare function useDebounceFn<T extends (...args: any[]) => any>(fn: T, delay: number): (...args: any[]) => void;
3
+ export declare const inBrowser: boolean;
@@ -0,0 +1,23 @@
1
+ const listDelimiterRE = /;(?![^(]*\))/g;
2
+ const propertyDelimiterRE = /:([^]+)/;
3
+ const styleCommentRE = /\/\*[^]*?\*\//g;
4
+ export function parseStringStyle(cssText) {
5
+ const ret = {};
6
+ cssText.replace(styleCommentRE, '').split(listDelimiterRE).forEach((item) => {
7
+ if (item) {
8
+ const tmp = item.split(propertyDelimiterRE);
9
+ tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim());
10
+ }
11
+ });
12
+ return ret;
13
+ }
14
+ export function useDebounceFn(fn, delay) {
15
+ let timer;
16
+ return (...args) => {
17
+ clearTimeout(timer);
18
+ timer = setTimeout(() => {
19
+ fn(...args);
20
+ }, delay);
21
+ };
22
+ }
23
+ export const inBrowser = typeof document !== 'undefined';
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/components/util.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAG,eAAe,CAAA;AACvC,MAAM,mBAAmB,GAAG,SAAS,CAAA;AACrC,MAAM,cAAc,GAAG,gBAAgB,CAAA;AAEvC,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,MAAM,GAAG,GAAoC,EAAE,CAAA;IAC/C,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC1E,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;YAC3C,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;QACxD,CAAC;IACH,CAAC,CAAC,CAAA;IACF,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,MAAM,UAAU,aAAa,CAAoC,EAAK,EAAE,KAAa;IACnF,IAAI,KAAU,CAAA;IACd,OAAO,CAAC,GAAG,IAAW,EAAE,EAAE;QACxB,YAAY,CAAC,KAAK,CAAC,CAAA;QACnB,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACtB,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;QACb,CAAC,EAAE,KAAK,CAAC,CAAA;IACX,CAAC,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,QAAQ,KAAK,WAAW,CAAA"}
@@ -0,0 +1,15 @@
1
+ import { PluginOption } from 'vite';
2
+
3
+ interface ArtalkPluginOptions {
4
+ site: string;
5
+ server: string;
6
+ mobileMinify?: boolean;
7
+ label?: string;
8
+ icon?: string;
9
+ showCommentBtn?: boolean;
10
+ [key: string]: any;
11
+ }
12
+
13
+ declare function artalkPlugin(options?: ArtalkPluginOptions): PluginOption;
14
+
15
+ export { ArtalkPluginOptions, artalkPlugin };
@@ -0,0 +1,15 @@
1
+ import { PluginOption } from 'vite';
2
+
3
+ interface ArtalkPluginOptions {
4
+ site: string;
5
+ server: string;
6
+ mobileMinify?: boolean;
7
+ label?: string;
8
+ icon?: string;
9
+ showCommentBtn?: boolean;
10
+ [key: string]: any;
11
+ }
12
+
13
+ declare function artalkPlugin(options?: ArtalkPluginOptions): PluginOption;
14
+
15
+ export { ArtalkPluginOptions, artalkPlugin };
package/dist/index.js ADDED
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ artalkPlugin: () => artalkPlugin
34
+ });
35
+ module.exports = __toCommonJS(src_exports);
36
+ var import_javascript_stringify = require("javascript-stringify");
37
+
38
+ // src/util.ts
39
+ var import_node_path = __toESM(require("path"));
40
+ var import_node_url = require("url");
41
+ var import_meta = {};
42
+ function isESM() {
43
+ return typeof __filename === "undefined" || typeof __dirname === "undefined";
44
+ }
45
+ function getDirname() {
46
+ return isESM() ? import_node_path.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url)) : __dirname;
47
+ }
48
+
49
+ // src/index.ts
50
+ var componentName = "ArtalkComment";
51
+ var componentFile = `${componentName}.vue`;
52
+ var aliasComponentFile = `${getDirname()}/components/${componentFile}`;
53
+ var virtualModuleId = "virtual:artalk-plugin-options";
54
+ var resolvedVirtualModuleId = `\0${virtualModuleId}`;
55
+ var slots = ["doc-after"];
56
+ function artalkPlugin(options) {
57
+ const componentOptions = {
58
+ ...options
59
+ };
60
+ let resolveConfig;
61
+ let vitepressConfig;
62
+ const pluginOps = {
63
+ name: "vitepress-plugin-artalk",
64
+ enforce: "pre",
65
+ configResolved(config) {
66
+ if (resolveConfig) {
67
+ return;
68
+ }
69
+ resolveConfig = config;
70
+ vitepressConfig = config.vitepress;
71
+ if (!vitepressConfig) {
72
+ return;
73
+ }
74
+ const selfTransformPageData = vitepressConfig.transformPageData;
75
+ vitepressConfig.transformPageData = async (pageData, ctx) => {
76
+ pageData.frontmatter.head ??= [];
77
+ pageData.frontmatter.head.push(...getArtalkScriptHead(options));
78
+ return selfTransformPageData?.(pageData, ctx);
79
+ };
80
+ },
81
+ config: () => {
82
+ return {
83
+ resolve: {
84
+ alias: {
85
+ [`./${componentFile}`]: aliasComponentFile
86
+ }
87
+ }
88
+ };
89
+ },
90
+ transform(code, id) {
91
+ if (id.endsWith("vitepress/dist/client/theme-default/Layout.vue")) {
92
+ let transformResult = code;
93
+ for (const element of slots) {
94
+ const slotPosition = `<slot name="${element}" />`;
95
+ transformResult = transformResult.replace(slotPosition, `${slotPosition}<ClientOnly><${componentName} /></ClientOnly>`);
96
+ }
97
+ const setupPosition = '<script setup lang="ts">';
98
+ transformResult = transformResult.replace(setupPosition, `${setupPosition}
99
+ import ${componentName} from './${componentName}.vue'`);
100
+ return transformResult;
101
+ }
102
+ },
103
+ resolveId(id) {
104
+ if (id === virtualModuleId) {
105
+ return resolvedVirtualModuleId;
106
+ }
107
+ },
108
+ load(id) {
109
+ if (id === resolvedVirtualModuleId) {
110
+ return `export default ${(0, import_javascript_stringify.stringify)(componentOptions)}`;
111
+ }
112
+ }
113
+ };
114
+ return pluginOps;
115
+ }
116
+ function getArtalkScriptHead(options) {
117
+ if (!options?.server) {
118
+ return [];
119
+ }
120
+ const { server } = options;
121
+ return [
122
+ ["link", { href: `${server}/dist/Artalk.css`, rel: "stylesheet" }],
123
+ ["script", { src: `${server}/dist/Artalk.js`, id: "artalk-script" }]
124
+ ];
125
+ }
126
+ // Annotate the CommonJS export names for ESM import in node:
127
+ 0 && (module.exports = {
128
+ artalkPlugin
129
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,93 @@
1
+ // src/index.ts
2
+ import { stringify } from "javascript-stringify";
3
+
4
+ // src/util.ts
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ function isESM() {
8
+ return typeof __filename === "undefined" || typeof __dirname === "undefined";
9
+ }
10
+ function getDirname() {
11
+ return isESM() ? path.dirname(fileURLToPath(import.meta.url)) : __dirname;
12
+ }
13
+
14
+ // src/index.ts
15
+ var componentName = "ArtalkComment";
16
+ var componentFile = `${componentName}.vue`;
17
+ var aliasComponentFile = `${getDirname()}/components/${componentFile}`;
18
+ var virtualModuleId = "virtual:artalk-plugin-options";
19
+ var resolvedVirtualModuleId = `\0${virtualModuleId}`;
20
+ var slots = ["doc-after"];
21
+ function artalkPlugin(options) {
22
+ const componentOptions = {
23
+ ...options
24
+ };
25
+ let resolveConfig;
26
+ let vitepressConfig;
27
+ const pluginOps = {
28
+ name: "vitepress-plugin-artalk",
29
+ enforce: "pre",
30
+ configResolved(config) {
31
+ if (resolveConfig) {
32
+ return;
33
+ }
34
+ resolveConfig = config;
35
+ vitepressConfig = config.vitepress;
36
+ if (!vitepressConfig) {
37
+ return;
38
+ }
39
+ const selfTransformPageData = vitepressConfig.transformPageData;
40
+ vitepressConfig.transformPageData = async (pageData, ctx) => {
41
+ pageData.frontmatter.head ??= [];
42
+ pageData.frontmatter.head.push(...getArtalkScriptHead(options));
43
+ return selfTransformPageData?.(pageData, ctx);
44
+ };
45
+ },
46
+ config: () => {
47
+ return {
48
+ resolve: {
49
+ alias: {
50
+ [`./${componentFile}`]: aliasComponentFile
51
+ }
52
+ }
53
+ };
54
+ },
55
+ transform(code, id) {
56
+ if (id.endsWith("vitepress/dist/client/theme-default/Layout.vue")) {
57
+ let transformResult = code;
58
+ for (const element of slots) {
59
+ const slotPosition = `<slot name="${element}" />`;
60
+ transformResult = transformResult.replace(slotPosition, `${slotPosition}<ClientOnly><${componentName} /></ClientOnly>`);
61
+ }
62
+ const setupPosition = '<script setup lang="ts">';
63
+ transformResult = transformResult.replace(setupPosition, `${setupPosition}
64
+ import ${componentName} from './${componentName}.vue'`);
65
+ return transformResult;
66
+ }
67
+ },
68
+ resolveId(id) {
69
+ if (id === virtualModuleId) {
70
+ return resolvedVirtualModuleId;
71
+ }
72
+ },
73
+ load(id) {
74
+ if (id === resolvedVirtualModuleId) {
75
+ return `export default ${stringify(componentOptions)}`;
76
+ }
77
+ }
78
+ };
79
+ return pluginOps;
80
+ }
81
+ function getArtalkScriptHead(options) {
82
+ if (!options?.server) {
83
+ return [];
84
+ }
85
+ const { server } = options;
86
+ return [
87
+ ["link", { href: `${server}/dist/Artalk.css`, rel: "stylesheet" }],
88
+ ["script", { src: `${server}/dist/Artalk.js`, id: "artalk-script" }]
89
+ ];
90
+ }
91
+ export {
92
+ artalkPlugin
93
+ };
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "vitepress-plugin-artalk",
3
+ "version": "0.1.0",
4
+ "description": "vitepress plugin artalk",
5
+ "author": "sugar",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/ATQQ/sugar-blog/tree/master/packages/vitepress-plugin-artalk",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/ATQQ/sugar-blog.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/ATQQ/sugar-blog/issues"
14
+ },
15
+ "keywords": [
16
+ "vitepress",
17
+ "plugin",
18
+ "artalk",
19
+ "comment"
20
+ ],
21
+ "exports": {
22
+ ".": {
23
+ "import": "./dist/index.mjs",
24
+ "require": "./dist/index.js"
25
+ }
26
+ },
27
+ "main": "dist/index.js",
28
+ "module": "dist/index.mjs",
29
+ "types": "dist/index.d.ts",
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "peerDependencies": {
34
+ "vitepress": "^1 || ^2"
35
+ },
36
+ "dependencies": {
37
+ "@vueuse/core": "^14.1.0",
38
+ "javascript-stringify": "^2.1.0"
39
+ },
40
+ "devDependencies": {
41
+ "artalk": "^2.8.5",
42
+ "chokidar": "^3.6.0",
43
+ "fs-extra": "^11.1.1",
44
+ "tinyglobby": "^0.2.6",
45
+ "tsup": " ^7.2.0",
46
+ "typescript": "^5.5.4",
47
+ "vite": "^5",
48
+ "vitepress": "2.0.0-alpha.16",
49
+ "vue": "^3.5.24"
50
+ },
51
+ "scripts": {
52
+ "dev": "pnpm run /^dev:.*/",
53
+ "dev:plugin": "npx tsup src/index.ts --dts --watch --format esm,cjs --external vitepress",
54
+ "dev:component": "tsc --sourcemap -w --preserveWatchOutput -p src/components",
55
+ "dev:watch": "node scripts/watchAndCopy.mjs",
56
+ "build": "pnpm run /^build:.*/",
57
+ "build:plugin": "npx tsup src/index.ts --dts --format esm,cjs --external vitepress --silent",
58
+ "build:component": "tsc -p src/components && node scripts/copyComponents.mjs"
59
+ }
60
+ }