123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- import type { MarkdownEnv, MarkdownRenderer } from 'vitepress';
- import crypto from 'node:crypto';
- import { readdirSync } from 'node:fs';
- import { join } from 'node:path';
- export const rawPathRegexp =
-
- /^(.+?(?:\.([\da-z]+))?)(#[\w-]+)?(?: ?{(\d+(?:[,-]\d+)*)? ?(\S+)?})? ?(?:\[(.+)])?$/;
- function rawPathToToken(rawPath: string) {
- const [
- filepath = '',
- extension = '',
- region = '',
- lines = '',
- lang = '',
- rawTitle = '',
- ] = (rawPathRegexp.exec(rawPath) || []).slice(1);
- const title = rawTitle || filepath.split('/').pop() || '';
- return { extension, filepath, lang, lines, region, title };
- }
- export const demoPreviewPlugin = (md: MarkdownRenderer) => {
- md.core.ruler.after('inline', 'demo-preview', (state) => {
- const insertComponentImport = (importString: string) => {
- const index = state.tokens.findIndex(
- (i) => i.type === 'html_block' && i.content.match(/<script setup>/g),
- );
- if (index === -1) {
- const importComponent = new state.Token('html_block', '', 0);
- importComponent.content = `<script setup>\n${importString}\n</script>\n`;
- state.tokens.splice(0, 0, importComponent);
- } else {
- if (state.tokens[index]) {
- const content = state.tokens[index].content;
- state.tokens[index].content = content.replace(
- '</script>',
- `${importString}\n</script>`,
- );
- }
- }
- };
-
- const regex = /<DemoPreview[^>]*\sdir="([^"]*)"/g;
-
- state.src = state.src.replaceAll(regex, (_match, dir) => {
- const componentDir = join(process.cwd(), 'src', dir).replaceAll(
- '\\',
- '/',
- );
- let childFiles: string[] = [];
- let dirExists = true;
- try {
- childFiles =
- readdirSync(componentDir, {
- encoding: 'utf8',
- recursive: false,
- withFileTypes: false,
- }) || [];
- } catch {
- dirExists = false;
- }
- if (!dirExists) {
- return '';
- }
- const uniqueWord = generateContentHash(componentDir);
- const ComponentName = `DemoComponent_${uniqueWord}`;
- insertComponentImport(
- `import ${ComponentName} from '${componentDir}/index.vue'`,
- );
- const { path: _path } = state.env as MarkdownEnv;
- const index = state.tokens.findIndex((i) => i.content.match(regex));
- if (!state.tokens[index]) {
- return '';
- }
- const firstString = 'index.vue';
- childFiles = childFiles.sort((a, b) => {
- if (a === firstString) return -1;
- if (b === firstString) return 1;
- return a.localeCompare(b, 'en', { sensitivity: 'base' });
- });
- state.tokens[index].content =
- `<DemoPreview files="${encodeURIComponent(JSON.stringify(childFiles))}" ><${ComponentName}/>
- `;
- const _dummyToken = new state.Token('', '', 0);
- const tokenArray: Array<typeof _dummyToken> = [];
- childFiles.forEach((filename) => {
-
- const templateStart = new state.Token('html_inline', '', 0);
- templateStart.content = `<template #${filename}>`;
- tokenArray.push(templateStart);
- const resolvedPath = join(componentDir, filename);
- const { extension, filepath, lang, lines, title } =
- rawPathToToken(resolvedPath);
-
- const token = new state.Token('fence', 'code', 0);
- token.info = `${lang || extension}${lines ? `{${lines}}` : ''}${
- title ? `[${title}]` : ''
- }`;
- token.content = `<<< ${filepath}`;
- (token as any).src = [resolvedPath];
- tokenArray.push(token);
- const templateEnd = new state.Token('html_inline', '', 0);
- templateEnd.content = '</template>';
- tokenArray.push(templateEnd);
- });
- const endTag = new state.Token('html_inline', '', 0);
- endTag.content = '</DemoPreview>';
- tokenArray.push(endTag);
- state.tokens.splice(index + 1, 0, ...tokenArray);
-
-
-
- return '';
- });
- });
- };
- function generateContentHash(input: string, length: number = 10): string {
-
- const hash = crypto.createHash('sha256').update(input).digest('hex');
-
- return Number.parseInt(hash, 16).toString(36).slice(0, length);
- }
|