@berlysia/vertical-writing-slide-system 0.0.31 → 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/package.json +17 -16
- package/src/types/gray-matter.d.ts +14 -0
- package/src/types/slide-metadata.js +1 -0
- package/src/types/slide-metadata.ts +29 -0
- package/src/virtual-slides.d.ts +6 -0
- package/src/vite-plugin-slides.ts +235 -10
- package/tsconfig.app.json +1 -0
- package/tsconfig.node.json +9 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@berlysia/vertical-writing-slide-system",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"vertical-slides": "./cli.js"
|
|
@@ -15,12 +15,27 @@
|
|
|
15
15
|
"src",
|
|
16
16
|
"index.html"
|
|
17
17
|
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "vite",
|
|
20
|
+
"build": "tsc -b && vite build",
|
|
21
|
+
"build:cli": "tsc --project tsconfig.cli.json",
|
|
22
|
+
"preversion": "pnpm build:cli",
|
|
23
|
+
"lint": "eslint .",
|
|
24
|
+
"preview": "vite preview",
|
|
25
|
+
"build:pages": "node scripts/build-pages.ts",
|
|
26
|
+
"test:vrt": "playwright test",
|
|
27
|
+
"test:vrt:clear": "rm -rf tests/__snapshots__",
|
|
28
|
+
"test:vrt:update": "playwright test --update-snapshots",
|
|
29
|
+
"ai:test:vrt": "AI=1 playwright test",
|
|
30
|
+
"ai:test:vrt:update": "AI=1 playwright test --update-snapshots"
|
|
31
|
+
},
|
|
18
32
|
"dependencies": {
|
|
19
33
|
"@emotion/react": "^11.14.0",
|
|
20
34
|
"@mdx-js/mdx": "^3.1.0",
|
|
21
35
|
"@mdx-js/react": "^3.1.0",
|
|
22
36
|
"@mdx-js/rollup": "^3.1.0",
|
|
23
37
|
"@vitejs/plugin-react-swc": "^3.5.0",
|
|
38
|
+
"gray-matter": "^4.0.3",
|
|
24
39
|
"prompts": "^2.4.2",
|
|
25
40
|
"react": "^19.0.0",
|
|
26
41
|
"react-dom": "^19.0.0",
|
|
@@ -54,19 +69,5 @@
|
|
|
54
69
|
"typescript": "~5.8.2",
|
|
55
70
|
"typescript-eslint": "^8.26.0",
|
|
56
71
|
"unist-util-visit": "^5.0.0"
|
|
57
|
-
},
|
|
58
|
-
"scripts": {
|
|
59
|
-
"dev": "vite",
|
|
60
|
-
"build": "tsc -b && vite build",
|
|
61
|
-
"build:cli": "tsc --project tsconfig.cli.json",
|
|
62
|
-
"preversion": "pnpm build:cli",
|
|
63
|
-
"lint": "eslint .",
|
|
64
|
-
"preview": "vite preview",
|
|
65
|
-
"build:pages": "node scripts/build-pages.ts",
|
|
66
|
-
"test:vrt": "playwright test",
|
|
67
|
-
"test:vrt:clear": "rm -rf tests/__snapshots__",
|
|
68
|
-
"test:vrt:update": "playwright test --update-snapshots",
|
|
69
|
-
"ai:test:vrt": "AI=1 playwright test",
|
|
70
|
-
"ai:test:vrt:update": "AI=1 playwright test --update-snapshots"
|
|
71
72
|
}
|
|
72
|
-
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare module "gray-matter" {
|
|
2
|
+
interface GrayMatterFile {
|
|
3
|
+
data: any;
|
|
4
|
+
content: string;
|
|
5
|
+
excerpt?: string;
|
|
6
|
+
orig: string | Buffer;
|
|
7
|
+
language: string;
|
|
8
|
+
matter: string;
|
|
9
|
+
stringify(lang?: string): string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function matter(input: string | Buffer): GrayMatterFile;
|
|
13
|
+
export = matter;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* スライドのメタデータ型定義
|
|
3
|
+
*/
|
|
4
|
+
export interface SlideMetadata {
|
|
5
|
+
/** スライドのタイトル */
|
|
6
|
+
title?: string;
|
|
7
|
+
/** スライドの説明 */
|
|
8
|
+
description?: string;
|
|
9
|
+
/** 作成者 */
|
|
10
|
+
author?: string;
|
|
11
|
+
/** 作成日時 */
|
|
12
|
+
date?: string;
|
|
13
|
+
/** OGP画像のパス(ogp.pngが存在する場合に自動設定) */
|
|
14
|
+
ogImage?: string;
|
|
15
|
+
/** その他のカスタムフィールド */
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* frontmatterをパースした結果
|
|
21
|
+
*/
|
|
22
|
+
export interface ParsedSlideFile {
|
|
23
|
+
/** パースされたメタデータ */
|
|
24
|
+
metadata: SlideMetadata;
|
|
25
|
+
/** メタデータを除いたコンテンツ */
|
|
26
|
+
content: string;
|
|
27
|
+
/** 元のファイル内容 */
|
|
28
|
+
originalContent: string;
|
|
29
|
+
}
|
package/src/virtual-slides.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
2
|
|
|
3
3
|
declare module "virtual:slides.js" {
|
|
4
|
+
import type { ParsedScript } from "./script-manager";
|
|
5
|
+
import type { SlideMetadata } from "./types/slide-metadata";
|
|
6
|
+
|
|
4
7
|
type SlideContent = string | (() => JSX.Element) | React.ComponentType;
|
|
5
8
|
const content: SlideContent[];
|
|
6
9
|
export default content;
|
|
10
|
+
|
|
11
|
+
export const slideScripts: ParsedScript;
|
|
12
|
+
export const slideMetadata: SlideMetadata;
|
|
7
13
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
Plugin,
|
|
3
|
+
ViteDevServer,
|
|
4
|
+
ResolvedConfig,
|
|
5
|
+
HtmlTagDescriptor,
|
|
6
|
+
} from "vite";
|
|
2
7
|
import * as fs from "node:fs";
|
|
3
8
|
import * as path from "node:path";
|
|
4
9
|
import { mkdirSync, readdirSync } from "node:fs";
|
|
@@ -14,6 +19,8 @@ import { ScriptManager, type ParsedScript } from "./script-manager";
|
|
|
14
19
|
import { visit } from "unist-util-visit";
|
|
15
20
|
import type { Node } from "unist";
|
|
16
21
|
import type { Element, Text, ElementContent } from "hast";
|
|
22
|
+
import matter from "gray-matter";
|
|
23
|
+
import type { SlideMetadata, ParsedSlideFile } from "./types/slide-metadata";
|
|
17
24
|
|
|
18
25
|
/**
|
|
19
26
|
* CSS抽出用のrehypeプラグイン
|
|
@@ -87,6 +94,22 @@ function loadAdjacentCSS(slidesDir: string, collection: string): string[] {
|
|
|
87
94
|
return [];
|
|
88
95
|
}
|
|
89
96
|
|
|
97
|
+
/**
|
|
98
|
+
* OGP画像ファイル(ogp.png)が存在するか確認
|
|
99
|
+
* 存在する場合はファイル名を返す
|
|
100
|
+
*/
|
|
101
|
+
function checkOgpImage(slidesDir: string, collection: string): string | null {
|
|
102
|
+
const collectionDir = path.resolve(slidesDir, collection);
|
|
103
|
+
const ogpPath = path.resolve(collectionDir, "ogp.png");
|
|
104
|
+
|
|
105
|
+
if (fs.existsSync(ogpPath)) {
|
|
106
|
+
logger.info("Found OGP image: ogp.png");
|
|
107
|
+
return "ogp.png";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
90
113
|
/**
|
|
91
114
|
* 隣接スクリプト設定ファイルを検索して読み込み
|
|
92
115
|
*/
|
|
@@ -124,6 +147,37 @@ function loadAdjacentScripts(
|
|
|
124
147
|
return result;
|
|
125
148
|
}
|
|
126
149
|
|
|
150
|
+
/**
|
|
151
|
+
* ファイル内容からfrontmatterをパースして、メタデータとコンテンツに分離
|
|
152
|
+
*/
|
|
153
|
+
function parseSlideFile(
|
|
154
|
+
content: string,
|
|
155
|
+
collectionName: string,
|
|
156
|
+
): ParsedSlideFile {
|
|
157
|
+
try {
|
|
158
|
+
const parsed = matter(content);
|
|
159
|
+
const metadata: SlideMetadata = {
|
|
160
|
+
// デフォルトタイトルとしてコレクション名を使用
|
|
161
|
+
title: collectionName,
|
|
162
|
+
...parsed.data,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
metadata,
|
|
167
|
+
content: parsed.content,
|
|
168
|
+
originalContent: content,
|
|
169
|
+
};
|
|
170
|
+
} catch (error) {
|
|
171
|
+
logger.warn(`Failed to parse frontmatter: ${error}`);
|
|
172
|
+
// パースに失敗した場合はfrontmatterなしとして扱う
|
|
173
|
+
return {
|
|
174
|
+
metadata: { title: collectionName },
|
|
175
|
+
content,
|
|
176
|
+
originalContent: content,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
127
181
|
export interface SlidesPluginOptions {
|
|
128
182
|
/** Directory containing the slides (absolute path) */
|
|
129
183
|
slidesDir?: string;
|
|
@@ -231,6 +285,7 @@ export default async function slidesPlugin(
|
|
|
231
285
|
let resolvedConfig: ResolvedConfig;
|
|
232
286
|
let slideStyles: string[] = [];
|
|
233
287
|
let slideScripts: ParsedScript = { external: [], inline: [] };
|
|
288
|
+
let slideMetadata: SlideMetadata = { title: config.collection };
|
|
234
289
|
return {
|
|
235
290
|
name: "vite-plugin-slides",
|
|
236
291
|
configResolved(config: ResolvedConfig) {
|
|
@@ -288,6 +343,19 @@ export default async function slidesPlugin(
|
|
|
288
343
|
|
|
289
344
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
290
345
|
|
|
346
|
+
// frontmatterをパースしてメタデータとコンテンツを分離
|
|
347
|
+
const parsedFile = parseSlideFile(content, config.collection);
|
|
348
|
+
slideMetadata = parsedFile.metadata;
|
|
349
|
+
const slideContent = parsedFile.content;
|
|
350
|
+
|
|
351
|
+
// OGP画像を検出してメタデータに設定
|
|
352
|
+
const ogpImageFile = checkOgpImage(config.slidesDir, config.collection);
|
|
353
|
+
if (ogpImageFile) {
|
|
354
|
+
slideMetadata.ogImage = `${base}slide-assets/${ogpImageFile}`;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
logger.info(`Slide metadata: ${JSON.stringify(slideMetadata)}`);
|
|
358
|
+
|
|
291
359
|
// 隣接CSSファイルを読み込み
|
|
292
360
|
const adjacentStyles = loadAdjacentCSS(
|
|
293
361
|
config.slidesDir,
|
|
@@ -306,7 +374,7 @@ export default async function slidesPlugin(
|
|
|
306
374
|
};
|
|
307
375
|
|
|
308
376
|
if (!isMdx) {
|
|
309
|
-
const slides =
|
|
377
|
+
const slides = slideContent.split(/^\s*(?:---|\*\*\*|___)\s*$/m);
|
|
310
378
|
const extractedStyles: string[] = [];
|
|
311
379
|
const extractedScripts: ParsedScript = { external: [], inline: [] };
|
|
312
380
|
const processedSlides = await Promise.all(
|
|
@@ -349,29 +417,29 @@ export default async function slidesPlugin(
|
|
|
349
417
|
`;
|
|
350
418
|
}
|
|
351
419
|
|
|
352
|
-
const slides =
|
|
420
|
+
const slides = slideContent.split(/^\s*(?:---|\*\*\*|___)\s*$/m);
|
|
353
421
|
|
|
354
422
|
// MDXにもCSS・スクリプト抽出を適用
|
|
355
423
|
const extractedStyles: string[] = [];
|
|
356
424
|
const extractedScripts: ParsedScript = { external: [], inline: [] };
|
|
357
425
|
const processedSlides = await Promise.all(
|
|
358
|
-
slides.map(async (
|
|
426
|
+
slides.map(async (slide) => {
|
|
359
427
|
// MDX内のstyleタグを手動で抽出(簡易実装)
|
|
360
428
|
const styleRegex = /<style[^>]*>([\s\S]*?)<\/style>/gi;
|
|
361
429
|
let match;
|
|
362
|
-
while ((match = styleRegex.exec(
|
|
430
|
+
while ((match = styleRegex.exec(slide)) !== null) {
|
|
363
431
|
if (match[1].trim()) {
|
|
364
432
|
extractedStyles.push(match[1].trim());
|
|
365
433
|
}
|
|
366
434
|
}
|
|
367
435
|
|
|
368
436
|
// MDX内のscriptタグを抽出
|
|
369
|
-
const parsedScripts = ScriptManager.parseScripts(
|
|
437
|
+
const parsedScripts = ScriptManager.parseScripts(slide);
|
|
370
438
|
extractedScripts.external.push(...parsedScripts.external);
|
|
371
439
|
extractedScripts.inline.push(...parsedScripts.inline);
|
|
372
440
|
|
|
373
441
|
// style・scriptタグを削除したコンテンツでMDXコンパイル
|
|
374
|
-
let cleanedContent =
|
|
442
|
+
let cleanedContent = slide.replace(styleRegex, "");
|
|
375
443
|
cleanedContent =
|
|
376
444
|
ScriptManager.removeScriptsFromContent(cleanedContent);
|
|
377
445
|
|
|
@@ -446,6 +514,9 @@ export default async function slidesPlugin(
|
|
|
446
514
|
// スライド固有のスクリプトを外部から利用可能にする
|
|
447
515
|
export const slideScripts = ${slideScriptsString};
|
|
448
516
|
|
|
517
|
+
// スライドのメタデータを外部から利用可能にする
|
|
518
|
+
export const slideMetadata = ${JSON.stringify(slideMetadata)};
|
|
519
|
+
|
|
449
520
|
// provide slide components to each slide
|
|
450
521
|
// Wrap SlideN components to provide SlideComponents
|
|
451
522
|
${compiledSlides
|
|
@@ -526,6 +597,29 @@ export default async function slidesPlugin(
|
|
|
526
597
|
} else {
|
|
527
598
|
logger.info(`No images directory found at: ${sourceImagesDir}`);
|
|
528
599
|
}
|
|
600
|
+
|
|
601
|
+
// OGP画像をコピー
|
|
602
|
+
const targetAssetsDir = isExternalCLI
|
|
603
|
+
? path.resolve(process.cwd(), "public/slide-assets")
|
|
604
|
+
: path.resolve(resolvedConfig.root, "public/slide-assets");
|
|
605
|
+
const sourceOgpPath = path.resolve(
|
|
606
|
+
config.slidesDir,
|
|
607
|
+
config.collection,
|
|
608
|
+
"ogp.png",
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
if (fs.existsSync(sourceOgpPath)) {
|
|
612
|
+
try {
|
|
613
|
+
mkdirSync(targetAssetsDir, { recursive: true });
|
|
614
|
+
const targetOgpPath = path.join(targetAssetsDir, "ogp.png");
|
|
615
|
+
await copyFile(sourceOgpPath, targetOgpPath);
|
|
616
|
+
logger.info("Copied OGP image: ogp.png");
|
|
617
|
+
} catch (error) {
|
|
618
|
+
if (error instanceof Error) {
|
|
619
|
+
logger.error("Failed to copy OGP image", error);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
529
623
|
}
|
|
530
624
|
},
|
|
531
625
|
|
|
@@ -533,12 +627,15 @@ export default async function slidesPlugin(
|
|
|
533
627
|
// Generate HTML file if none exists in consumer project
|
|
534
628
|
const consumerIndexHtml = path.resolve(resolvedConfig.root, "index.html");
|
|
535
629
|
|
|
536
|
-
|
|
630
|
+
// CLIモードでは常にメタデータ付きHTMLを生成
|
|
631
|
+
const isExternalCLI = process.cwd() !== resolvedConfig.root;
|
|
632
|
+
|
|
633
|
+
if (!fs.existsSync(consumerIndexHtml) || isExternalCLI) {
|
|
537
634
|
// Find the main JS and CSS files in the bundle
|
|
538
635
|
const mainJsFile = Object.keys(bundle).find(
|
|
539
636
|
(fileName) =>
|
|
540
637
|
fileName.startsWith("assets/") &&
|
|
541
|
-
fileName.includes("main-") &&
|
|
638
|
+
(fileName.includes("index-") || fileName.includes("main-")) &&
|
|
542
639
|
fileName.endsWith(".js"),
|
|
543
640
|
);
|
|
544
641
|
const mainCssFile = Object.keys(bundle).find(
|
|
@@ -548,6 +645,7 @@ export default async function slidesPlugin(
|
|
|
548
645
|
|
|
549
646
|
if (!mainJsFile) {
|
|
550
647
|
logger.error("Could not find main JS file in bundle");
|
|
648
|
+
logger.error("Available bundle files: " + Object.keys(bundle));
|
|
551
649
|
return;
|
|
552
650
|
}
|
|
553
651
|
|
|
@@ -555,12 +653,31 @@ export default async function slidesPlugin(
|
|
|
555
653
|
? `<link rel="stylesheet" href="./${mainCssFile}">`
|
|
556
654
|
: "<!-- CSS is included in the JS bundle -->";
|
|
557
655
|
|
|
656
|
+
const pageTitle = slideMetadata?.title || "Vertical Writing Slides";
|
|
657
|
+
|
|
658
|
+
// OGPメタタグを生成
|
|
659
|
+
const ogpTags: string[] = [];
|
|
660
|
+
if (slideMetadata?.title) {
|
|
661
|
+
ogpTags.push(`<meta property="og:title" content="${slideMetadata.title}" />`);
|
|
662
|
+
}
|
|
663
|
+
if (slideMetadata?.description) {
|
|
664
|
+
ogpTags.push(`<meta property="og:description" content="${slideMetadata.description}" />`);
|
|
665
|
+
}
|
|
666
|
+
if (slideMetadata?.ogImage) {
|
|
667
|
+
ogpTags.push(`<meta property="og:image" content="${slideMetadata.ogImage}" />`);
|
|
668
|
+
ogpTags.push(`<meta name="twitter:card" content="summary_large_image" />`);
|
|
669
|
+
ogpTags.push(`<meta name="twitter:image" content="${slideMetadata.ogImage}" />`);
|
|
670
|
+
}
|
|
671
|
+
|
|
558
672
|
const virtualIndexHtml = `<!doctype html>
|
|
559
673
|
<html lang="ja">
|
|
560
674
|
<head>
|
|
561
675
|
<meta charset="UTF-8" />
|
|
562
676
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
563
|
-
<title
|
|
677
|
+
<title>${pageTitle}</title>
|
|
678
|
+
${slideMetadata?.description ? `<meta name="description" content="${slideMetadata.description}" />` : ""}
|
|
679
|
+
${slideMetadata?.author ? `<meta name="author" content="${slideMetadata.author}" />` : ""}
|
|
680
|
+
${ogpTags.join("\n ")}
|
|
564
681
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
565
682
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
566
683
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&family=Noto+Sans+Mono:wght@100..900&display=swap" rel="stylesheet">
|
|
@@ -633,6 +750,30 @@ export default async function slidesPlugin(
|
|
|
633
750
|
} else {
|
|
634
751
|
logger.warn(`No images directory found at: ${sourceImagesDir}`);
|
|
635
752
|
}
|
|
753
|
+
|
|
754
|
+
// OGP画像をバンドルに追加
|
|
755
|
+
const sourceOgpPath = path.resolve(
|
|
756
|
+
config.slidesDir,
|
|
757
|
+
config.collection,
|
|
758
|
+
"ogp.png",
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
if (fs.existsSync(sourceOgpPath)) {
|
|
762
|
+
try {
|
|
763
|
+
const ogpContent = fs.readFileSync(sourceOgpPath);
|
|
764
|
+
this.emitFile({
|
|
765
|
+
type: "asset",
|
|
766
|
+
fileName: "slide-assets/ogp.png",
|
|
767
|
+
source: ogpContent,
|
|
768
|
+
});
|
|
769
|
+
logger.info("Added OGP image to bundle: ogp.png");
|
|
770
|
+
} catch (error) {
|
|
771
|
+
logger.error(
|
|
772
|
+
"Failed to add OGP image to bundle",
|
|
773
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
636
777
|
}
|
|
637
778
|
},
|
|
638
779
|
|
|
@@ -713,5 +854,89 @@ export default async function slidesPlugin(
|
|
|
713
854
|
// No additional cleanup needed since we're using Vite's built-in watcher
|
|
714
855
|
};
|
|
715
856
|
},
|
|
857
|
+
|
|
858
|
+
transformIndexHtml(html: string) {
|
|
859
|
+
// 既存の <title> タグを削除
|
|
860
|
+
const htmlWithoutTitle = html.replace(/<title>.*?<\/title>/i, "");
|
|
861
|
+
|
|
862
|
+
// メタデータタグを構築
|
|
863
|
+
const title = slideMetadata?.title || config.collection;
|
|
864
|
+
const tags: HtmlTagDescriptor[] = [
|
|
865
|
+
{
|
|
866
|
+
tag: "title",
|
|
867
|
+
children: title,
|
|
868
|
+
},
|
|
869
|
+
];
|
|
870
|
+
|
|
871
|
+
if (slideMetadata?.description) {
|
|
872
|
+
tags.push({
|
|
873
|
+
tag: "meta",
|
|
874
|
+
attrs: {
|
|
875
|
+
name: "description",
|
|
876
|
+
content: slideMetadata.description,
|
|
877
|
+
},
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
if (slideMetadata?.author) {
|
|
882
|
+
tags.push({
|
|
883
|
+
tag: "meta",
|
|
884
|
+
attrs: {
|
|
885
|
+
name: "author",
|
|
886
|
+
content: slideMetadata.author,
|
|
887
|
+
},
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// OGPメタタグを追加
|
|
892
|
+
if (slideMetadata?.title) {
|
|
893
|
+
tags.push({
|
|
894
|
+
tag: "meta",
|
|
895
|
+
attrs: {
|
|
896
|
+
property: "og:title",
|
|
897
|
+
content: slideMetadata.title,
|
|
898
|
+
},
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
if (slideMetadata?.description) {
|
|
903
|
+
tags.push({
|
|
904
|
+
tag: "meta",
|
|
905
|
+
attrs: {
|
|
906
|
+
property: "og:description",
|
|
907
|
+
content: slideMetadata.description,
|
|
908
|
+
},
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
if (slideMetadata?.ogImage) {
|
|
913
|
+
tags.push({
|
|
914
|
+
tag: "meta",
|
|
915
|
+
attrs: {
|
|
916
|
+
property: "og:image",
|
|
917
|
+
content: slideMetadata.ogImage,
|
|
918
|
+
},
|
|
919
|
+
});
|
|
920
|
+
tags.push({
|
|
921
|
+
tag: "meta",
|
|
922
|
+
attrs: {
|
|
923
|
+
name: "twitter:card",
|
|
924
|
+
content: "summary_large_image",
|
|
925
|
+
},
|
|
926
|
+
});
|
|
927
|
+
tags.push({
|
|
928
|
+
tag: "meta",
|
|
929
|
+
attrs: {
|
|
930
|
+
name: "twitter:image",
|
|
931
|
+
content: slideMetadata.ogImage,
|
|
932
|
+
},
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return {
|
|
937
|
+
html: htmlWithoutTitle,
|
|
938
|
+
tags,
|
|
939
|
+
};
|
|
940
|
+
},
|
|
716
941
|
};
|
|
717
942
|
}
|
package/tsconfig.app.json
CHANGED
package/tsconfig.node.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
+
"composite": true,
|
|
3
4
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
5
|
"target": "ES2022",
|
|
5
|
-
"lib": ["ES2023"],
|
|
6
|
+
"lib": ["ES2023", "DOM"],
|
|
6
7
|
"module": "ESNext",
|
|
7
8
|
"skipLibCheck": true,
|
|
8
9
|
|
|
@@ -20,5 +21,11 @@
|
|
|
20
21
|
"noFallthroughCasesInSwitch": true,
|
|
21
22
|
"noUncheckedSideEffectImports": true
|
|
22
23
|
},
|
|
23
|
-
"include": [
|
|
24
|
+
"include": [
|
|
25
|
+
"vite.config.ts",
|
|
26
|
+
"src/vite-plugin-slides.ts",
|
|
27
|
+
"src/remark-slide-images.ts",
|
|
28
|
+
"src/script-manager.ts",
|
|
29
|
+
"src/types/**/*.ts"
|
|
30
|
+
]
|
|
24
31
|
}
|