@berlysia/vertical-writing-slide-system 0.0.10 → 0.0.12
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 +6 -2
- package/package.json +1 -1
- package/scripts/build-pages.js +3 -3
- package/src/remark-slide-images.ts +16 -9
- package/src/vite-plugin-slides.ts +132 -29
- package/vite.config.ts +4 -1
package/cli.js
CHANGED
|
@@ -30,9 +30,13 @@ 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"),
|
|
37
|
+
rollupOptions: {
|
|
38
|
+
input: resolve(import.meta.dirname, "src/main.tsx"),
|
|
39
|
+
},
|
|
36
40
|
},
|
|
37
41
|
});
|
|
38
42
|
} catch (error) {
|
package/package.json
CHANGED
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) {
|
|
@@ -169,7 +171,7 @@ export default async function slidesPlugin(
|
|
|
169
171
|
nullPrefixedVirtualFilePageIdPattern.test(id)
|
|
170
172
|
) {
|
|
171
173
|
return {
|
|
172
|
-
code: `import React from 'react';\n${code}`,
|
|
174
|
+
code: `import * as React from 'react';\n${code}`,
|
|
173
175
|
map: null,
|
|
174
176
|
};
|
|
175
177
|
}
|
|
@@ -220,6 +222,8 @@ export default async function slidesPlugin(
|
|
|
220
222
|
const result = await compile(slideContent, {
|
|
221
223
|
outputFormat: "program",
|
|
222
224
|
development: false,
|
|
225
|
+
jsxImportSource: "react",
|
|
226
|
+
jsxRuntime: "automatic",
|
|
223
227
|
remarkPlugins: [[remarkSlideImages, { base }]],
|
|
224
228
|
});
|
|
225
229
|
return result.value as string;
|
|
@@ -282,35 +286,134 @@ export default async function slidesPlugin(
|
|
|
282
286
|
}
|
|
283
287
|
},
|
|
284
288
|
async buildStart() {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const
|
|
305
|
-
const
|
|
306
|
-
|
|
289
|
+
// Handle images during dev mode
|
|
290
|
+
if (resolvedConfig.command === "serve") {
|
|
291
|
+
const targetImagesDir = path.resolve(
|
|
292
|
+
resolvedConfig.root,
|
|
293
|
+
"public/slide-assets/images",
|
|
294
|
+
);
|
|
295
|
+
const sourceImagesDir = path.resolve(
|
|
296
|
+
config.slidesDir,
|
|
297
|
+
config.collection,
|
|
298
|
+
"images",
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
// Copy images from slides directory
|
|
302
|
+
if (fs.existsSync(sourceImagesDir)) {
|
|
303
|
+
try {
|
|
304
|
+
// Create target directory if it doesn't exist
|
|
305
|
+
mkdirSync(targetImagesDir, { recursive: true });
|
|
306
|
+
|
|
307
|
+
// Copy all files from source to target
|
|
308
|
+
const imageFiles = readdirSync(sourceImagesDir);
|
|
309
|
+
for (const file of imageFiles) {
|
|
310
|
+
const sourcePath = path.join(sourceImagesDir, file);
|
|
311
|
+
const targetPath = path.join(targetImagesDir, file);
|
|
312
|
+
copyFileSync(sourcePath, targetPath);
|
|
313
|
+
}
|
|
314
|
+
logger.info("Copied slide images successfully");
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (error instanceof Error) {
|
|
317
|
+
logger.error("Failed to copy slide images", error);
|
|
318
|
+
}
|
|
319
|
+
throw error;
|
|
307
320
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
generateBundle(options, bundle) {
|
|
326
|
+
// Generate HTML file if none exists in consumer project
|
|
327
|
+
const consumerIndexHtml = path.resolve(resolvedConfig.root, "index.html");
|
|
328
|
+
|
|
329
|
+
if (!fs.existsSync(consumerIndexHtml)) {
|
|
330
|
+
// Find the main JS file in the bundle
|
|
331
|
+
const mainJsFile = Object.keys(bundle).find(
|
|
332
|
+
(fileName) =>
|
|
333
|
+
fileName.startsWith("assets/main-") && fileName.endsWith(".js"),
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
if (!mainJsFile) {
|
|
337
|
+
logger.error("Could not find main JS file in bundle");
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const virtualIndexHtml = `<!doctype html>
|
|
342
|
+
<html lang="ja">
|
|
343
|
+
<head>
|
|
344
|
+
<meta charset="UTF-8" />
|
|
345
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
346
|
+
<title>Vertical Writing Slides</title>
|
|
347
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
348
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
349
|
+
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&family=Noto+Sans+Mono:wght@100..900&display=swap" rel="stylesheet">
|
|
350
|
+
</head>
|
|
351
|
+
<body>
|
|
352
|
+
<div id="root"></div>
|
|
353
|
+
<script type="module" src="./${mainJsFile}"></script>
|
|
354
|
+
</body>
|
|
355
|
+
</html>`;
|
|
356
|
+
|
|
357
|
+
// Emit HTML file as part of the build output
|
|
358
|
+
this.emitFile({
|
|
359
|
+
type: "asset",
|
|
360
|
+
fileName: "index.html",
|
|
361
|
+
source: virtualIndexHtml,
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
logger.info("Generated index.html for build output");
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Handle images during build mode
|
|
368
|
+
if (resolvedConfig.command === "build") {
|
|
369
|
+
const sourceImagesDir = path.resolve(
|
|
370
|
+
config.slidesDir,
|
|
371
|
+
config.collection,
|
|
372
|
+
"images",
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
if (fs.existsSync(sourceImagesDir)) {
|
|
376
|
+
try {
|
|
377
|
+
const imageFiles = readdirSync(sourceImagesDir);
|
|
378
|
+
logger.info(
|
|
379
|
+
`Processing ${imageFiles.length} image files from ${sourceImagesDir}`,
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
let processedCount = 0;
|
|
383
|
+
for (const file of imageFiles) {
|
|
384
|
+
const sourcePath = path.join(sourceImagesDir, file);
|
|
385
|
+
try {
|
|
386
|
+
const imageContent = fs.readFileSync(sourcePath);
|
|
387
|
+
|
|
388
|
+
// Add image files to the bundle
|
|
389
|
+
this.emitFile({
|
|
390
|
+
type: "asset",
|
|
391
|
+
fileName: `slide-assets/images/${file}`,
|
|
392
|
+
source: imageContent,
|
|
393
|
+
});
|
|
394
|
+
processedCount++;
|
|
395
|
+
logger.info(`Added image to bundle: ${file}`);
|
|
396
|
+
} catch (fileError) {
|
|
397
|
+
logger.error(
|
|
398
|
+
`Failed to process image file: ${file}`,
|
|
399
|
+
fileError instanceof Error
|
|
400
|
+
? fileError
|
|
401
|
+
: new Error(String(fileError)),
|
|
402
|
+
);
|
|
403
|
+
// Continue processing other files instead of failing completely
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
logger.info(
|
|
407
|
+
`Successfully added ${processedCount}/${imageFiles.length} slide images to bundle`,
|
|
408
|
+
);
|
|
409
|
+
} catch (error) {
|
|
410
|
+
if (error instanceof Error) {
|
|
411
|
+
logger.error("Failed to read slide images directory", error);
|
|
412
|
+
}
|
|
413
|
+
throw error;
|
|
312
414
|
}
|
|
313
|
-
|
|
415
|
+
} else {
|
|
416
|
+
logger.warn(`No images directory found at: ${sourceImagesDir}`);
|
|
314
417
|
}
|
|
315
418
|
}
|
|
316
419
|
},
|
package/vite.config.ts
CHANGED