@berlysia/vertical-writing-slide-system 0.0.9 → 0.0.11
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/cli.js +3 -2
- package/package.json +2 -2
- package/scripts/build-pages.js +3 -3
- package/src/remark-slide-images.ts +16 -9
- package/src/vite-plugin-slides.ts +124 -25
- package/vite.config.ts +4 -1
package/cli.js
CHANGED
|
@@ -30,9 +30,10 @@ async function runBuildAll() {
|
|
|
30
30
|
async function runBuild() {
|
|
31
31
|
try {
|
|
32
32
|
await build({
|
|
33
|
-
root:
|
|
33
|
+
root: process.cwd(),
|
|
34
|
+
configFile: resolve(import.meta.dirname, "vite.config.ts"),
|
|
34
35
|
build: {
|
|
35
|
-
outDir: resolve("pages"),
|
|
36
|
+
outDir: resolve(process.cwd(), "pages"),
|
|
36
37
|
},
|
|
37
38
|
});
|
|
38
39
|
} catch (error) {
|
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.0.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"vertical-slides": "./cli.js"
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"@mdx-js/rollup": "^3.1.0",
|
|
22
22
|
"@mdx-js/mdx": "^3.1.0",
|
|
23
23
|
"@vitejs/plugin-react-swc": "^3.5.0",
|
|
24
|
+
"prompts": "^2.4.2",
|
|
24
25
|
"react": "^19.0.0",
|
|
25
26
|
"react-dom": "^19.0.0",
|
|
26
27
|
"rehype-react": "^8.0.0",
|
|
@@ -48,7 +49,6 @@
|
|
|
48
49
|
"globals": "^16.0.0",
|
|
49
50
|
"playwright-core": "^1.51.0",
|
|
50
51
|
"prettier": "^3.5.3",
|
|
51
|
-
"prompts": "^2.4.2",
|
|
52
52
|
"typescript": "~5.8.2",
|
|
53
53
|
"typescript-eslint": "^8.26.0",
|
|
54
54
|
"unist-util-visit": "^5.0.0"
|
package/scripts/build-pages.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { existsSync } from "fs";
|
|
2
|
-
import { mkdir, readdir, stat, cp, writeFile, mkdtemp,
|
|
2
|
+
import { mkdir, readdir, stat, cp, writeFile, mkdtemp, rm } from "fs/promises";
|
|
3
3
|
import { join, resolve } from "path";
|
|
4
4
|
import { build } from "vite";
|
|
5
5
|
const defaultSlidesDir = resolve(import.meta.dirname, "..", "slides");
|
|
6
6
|
const pagesDir = "pages";
|
|
7
7
|
// Ensure pages directory exists
|
|
8
|
-
await
|
|
8
|
+
await rm(pagesDir, { recursive: true });
|
|
9
9
|
await mkdir(pagesDir, { recursive: true });
|
|
10
10
|
async function buildSlide(slideName) {
|
|
11
11
|
console.log(`Building ${slideName}...`);
|
|
@@ -20,7 +20,7 @@ async function buildSlide(slideName) {
|
|
|
20
20
|
const slideOutputDir = join(pagesDir, slideName);
|
|
21
21
|
await mkdir(slideOutputDir, { recursive: true });
|
|
22
22
|
await cp(tmpDir, slideOutputDir, { recursive: true });
|
|
23
|
-
await
|
|
23
|
+
await rm(tmpDir, { recursive: true });
|
|
24
24
|
}
|
|
25
25
|
async function createIndexPage(slideNames) {
|
|
26
26
|
const slides = slideNames
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Plugin } from "unified";
|
|
2
2
|
import { visit } from "unist-util-visit";
|
|
3
|
-
import type { Root } from "mdast";
|
|
3
|
+
import type { Root, Image } from "mdast";
|
|
4
4
|
|
|
5
5
|
interface RemarkSlideImagesOptions {
|
|
6
6
|
base: string;
|
|
@@ -12,17 +12,22 @@ const remarkSlideImages: Plugin<[RemarkSlideImagesOptions], Root> = (
|
|
|
12
12
|
const { base } = options;
|
|
13
13
|
|
|
14
14
|
return (tree) => {
|
|
15
|
-
visit(tree, (node
|
|
15
|
+
visit(tree, (node) => {
|
|
16
16
|
// Handle Markdown image syntax
|
|
17
|
-
if (node.type === "image"
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
if (node.type === "image") {
|
|
18
|
+
const imageNode = node as Image;
|
|
19
|
+
if (imageNode.url.startsWith("@slide/")) {
|
|
20
|
+
imageNode.url = `${base}slide-assets/images/${imageNode.url.slice(7)}`;
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
// Handle MDX JSX img elements
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
+
if (node.type === "mdxJsxFlowElement" && (node as any).name === "img") {
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
const mdxNode = node as any;
|
|
29
|
+
const src = mdxNode.attributes?.find(
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
31
|
(attr: any) => attr.type === "mdxJsxAttribute" && attr.name === "src",
|
|
27
32
|
);
|
|
28
33
|
if (
|
|
@@ -36,9 +41,11 @@ const remarkSlideImages: Plugin<[RemarkSlideImagesOptions], Root> = (
|
|
|
36
41
|
|
|
37
42
|
// Handle HTML img tags in Markdown
|
|
38
43
|
if (node.type === "html") {
|
|
39
|
-
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
const htmlNode = node as any;
|
|
46
|
+
const value = htmlNode.value as string;
|
|
40
47
|
if (value.startsWith("<img")) {
|
|
41
|
-
|
|
48
|
+
htmlNode.value = value.replace(
|
|
42
49
|
/<img\s+([^>]*src="(@slide\/[^"]+)"[^>]*)>/g,
|
|
43
50
|
(_, attributes, src) => {
|
|
44
51
|
return `<img ${attributes.replace(
|
|
@@ -50,7 +50,7 @@ function validateSlidesDir(dir: string): void {
|
|
|
50
50
|
}
|
|
51
51
|
try {
|
|
52
52
|
fs.accessSync(dir, fs.constants.R_OK);
|
|
53
|
-
} catch
|
|
53
|
+
} catch {
|
|
54
54
|
throw new Error(`No read permission for external slides directory: ${dir}`);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -149,10 +149,12 @@ export default async function slidesPlugin(
|
|
|
149
149
|
|
|
150
150
|
let base: string;
|
|
151
151
|
let compiledSlides: string[] = [];
|
|
152
|
+
let resolvedConfig: ResolvedConfig;
|
|
152
153
|
return {
|
|
153
154
|
name: "vite-plugin-slides",
|
|
154
155
|
configResolved(config: ResolvedConfig) {
|
|
155
156
|
base = config.base;
|
|
157
|
+
resolvedConfig = config;
|
|
156
158
|
},
|
|
157
159
|
enforce: "pre",
|
|
158
160
|
resolveId(id: string) {
|
|
@@ -282,37 +284,134 @@ export default async function slidesPlugin(
|
|
|
282
284
|
}
|
|
283
285
|
},
|
|
284
286
|
async buildStart() {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
)
|
|
289
|
-
const sourceImagesDir = path.resolve(
|
|
290
|
-
config.slidesDir,
|
|
291
|
-
config.collection,
|
|
292
|
-
"images",
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
// Copy images from slides directory
|
|
296
|
-
if (fs.existsSync(sourceImagesDir)) {
|
|
287
|
+
// Ensure index.html exists in consumer project
|
|
288
|
+
const consumerIndexHtml = path.resolve(resolvedConfig.root, "index.html");
|
|
289
|
+
|
|
290
|
+
if (!fs.existsSync(consumerIndexHtml)) {
|
|
297
291
|
try {
|
|
298
|
-
// Create
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
292
|
+
// Create a consumer-specific index.html that points to library sources
|
|
293
|
+
const libraryRoot = path.resolve(import.meta.dirname, "..");
|
|
294
|
+
const relativePath = path.relative(resolvedConfig.root, libraryRoot);
|
|
295
|
+
|
|
296
|
+
const indexHtmlContent = `<!doctype html>
|
|
297
|
+
<html lang="ja">
|
|
298
|
+
<head>
|
|
299
|
+
<meta charset="UTF-8" />
|
|
300
|
+
<link rel="icon" type="image/svg+xml" href="/${relativePath}/vite.svg" />
|
|
301
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
302
|
+
<title>Vertical Writing Slides</title>
|
|
303
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
304
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
305
|
+
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&family=Noto+Sans+Mono:wght@100..900&display=swap" rel="stylesheet">
|
|
306
|
+
<link rel="stylesheet" href="/${relativePath}/src/index.css" />
|
|
307
|
+
<link rel="stylesheet" media="screen" href="/${relativePath}/src/screen.css" />
|
|
308
|
+
<link rel="stylesheet" media="print" href="/${relativePath}/src/print.css" />
|
|
309
|
+
</head>
|
|
310
|
+
<body>
|
|
311
|
+
<div id="root"></div>
|
|
312
|
+
<script type="module" src="/${relativePath}/src/main.tsx"></script>
|
|
313
|
+
</body>
|
|
314
|
+
</html>`;
|
|
315
|
+
|
|
316
|
+
fs.writeFileSync(consumerIndexHtml, indexHtmlContent);
|
|
317
|
+
logger.info("Generated index.html for consumer project");
|
|
309
318
|
} catch (error) {
|
|
310
319
|
if (error instanceof Error) {
|
|
311
|
-
logger.error("Failed to
|
|
320
|
+
logger.error("Failed to create index.html", error);
|
|
312
321
|
}
|
|
313
322
|
throw error;
|
|
314
323
|
}
|
|
315
324
|
}
|
|
325
|
+
|
|
326
|
+
// Handle images during dev mode
|
|
327
|
+
if (resolvedConfig.command === "serve") {
|
|
328
|
+
const targetImagesDir = path.resolve(
|
|
329
|
+
resolvedConfig.root,
|
|
330
|
+
"public/slide-assets/images",
|
|
331
|
+
);
|
|
332
|
+
const sourceImagesDir = path.resolve(
|
|
333
|
+
config.slidesDir,
|
|
334
|
+
config.collection,
|
|
335
|
+
"images",
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
// Copy images from slides directory
|
|
339
|
+
if (fs.existsSync(sourceImagesDir)) {
|
|
340
|
+
try {
|
|
341
|
+
// Create target directory if it doesn't exist
|
|
342
|
+
mkdirSync(targetImagesDir, { recursive: true });
|
|
343
|
+
|
|
344
|
+
// Copy all files from source to target
|
|
345
|
+
const imageFiles = readdirSync(sourceImagesDir);
|
|
346
|
+
for (const file of imageFiles) {
|
|
347
|
+
const sourcePath = path.join(sourceImagesDir, file);
|
|
348
|
+
const targetPath = path.join(targetImagesDir, file);
|
|
349
|
+
copyFileSync(sourcePath, targetPath);
|
|
350
|
+
}
|
|
351
|
+
logger.info("Copied slide images successfully");
|
|
352
|
+
} catch (error) {
|
|
353
|
+
if (error instanceof Error) {
|
|
354
|
+
logger.error("Failed to copy slide images", error);
|
|
355
|
+
}
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
generateBundle() {
|
|
363
|
+
// Handle images during build mode
|
|
364
|
+
if (resolvedConfig.command === "build") {
|
|
365
|
+
const sourceImagesDir = path.resolve(
|
|
366
|
+
config.slidesDir,
|
|
367
|
+
config.collection,
|
|
368
|
+
"images",
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
if (fs.existsSync(sourceImagesDir)) {
|
|
372
|
+
try {
|
|
373
|
+
const imageFiles = readdirSync(sourceImagesDir);
|
|
374
|
+
logger.info(
|
|
375
|
+
`Processing ${imageFiles.length} image files from ${sourceImagesDir}`,
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
let processedCount = 0;
|
|
379
|
+
for (const file of imageFiles) {
|
|
380
|
+
const sourcePath = path.join(sourceImagesDir, file);
|
|
381
|
+
try {
|
|
382
|
+
const imageContent = fs.readFileSync(sourcePath);
|
|
383
|
+
|
|
384
|
+
// Add image files to the bundle
|
|
385
|
+
this.emitFile({
|
|
386
|
+
type: "asset",
|
|
387
|
+
fileName: `slide-assets/images/${file}`,
|
|
388
|
+
source: imageContent,
|
|
389
|
+
});
|
|
390
|
+
processedCount++;
|
|
391
|
+
logger.info(`Added image to bundle: ${file}`);
|
|
392
|
+
} catch (fileError) {
|
|
393
|
+
logger.error(
|
|
394
|
+
`Failed to process image file: ${file}`,
|
|
395
|
+
fileError instanceof Error
|
|
396
|
+
? fileError
|
|
397
|
+
: new Error(String(fileError)),
|
|
398
|
+
);
|
|
399
|
+
// Continue processing other files instead of failing completely
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
logger.info(
|
|
403
|
+
`Successfully added ${processedCount}/${imageFiles.length} slide images to bundle`,
|
|
404
|
+
);
|
|
405
|
+
} catch (error) {
|
|
406
|
+
if (error instanceof Error) {
|
|
407
|
+
logger.error("Failed to read slide images directory", error);
|
|
408
|
+
}
|
|
409
|
+
throw error;
|
|
410
|
+
}
|
|
411
|
+
} else {
|
|
412
|
+
logger.warn(`No images directory found at: ${sourceImagesDir}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
316
415
|
},
|
|
317
416
|
|
|
318
417
|
configureServer(server: ViteDevServer) {
|
package/vite.config.ts
CHANGED