vite-plugin-react-deck 1.5.2 → 1.6.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.
Files changed (4) hide show
  1. package/index.cjs +59 -13
  2. package/index.mjs +59 -13
  3. package/package.json +1 -1
  4. package/slides.d.ts +20 -2
package/index.cjs CHANGED
@@ -427,6 +427,7 @@ document.head.appendChild(link)
427
427
 
428
428
  // src/slides.ts
429
429
  var import_node_fs = __toESM(require("fs"), 1);
430
+ var import_node_path = __toESM(require("path"), 1);
430
431
  var import_mdx = require("@mdx-js/mdx");
431
432
  var import_gray_matter = __toESM(require("gray-matter"), 1);
432
433
  var import_remark_directive = __toESM(require("remark-directive"), 1);
@@ -469,6 +470,39 @@ ${header}
469
470
  }
470
471
 
471
472
  // src/slides.ts
473
+ var INCLUDE_RE = /^::include\{file=(.+?)\}\s*$/gm;
474
+ function resolveIncludes(content, filePath, { maxDepth = 10, _depth = 0 } = {}) {
475
+ const includedFiles = [];
476
+ const dir = import_node_path.default.dirname(filePath);
477
+ const resolved = content.replace(
478
+ INCLUDE_RE,
479
+ (_match, includePath) => {
480
+ if (_depth >= maxDepth) {
481
+ throw new Error(
482
+ `Include depth limit (${maxDepth}) exceeded when including "${includePath}" from "${filePath}". Check for circular includes.`
483
+ );
484
+ }
485
+ const absolutePath = import_node_path.default.resolve(dir, includePath);
486
+ let fileContent;
487
+ try {
488
+ fileContent = import_node_fs.default.readFileSync(absolutePath, "utf-8");
489
+ } catch {
490
+ throw new Error(
491
+ `Could not read included file "${includePath}" (resolved to "${absolutePath}") from "${filePath}".`
492
+ );
493
+ }
494
+ includedFiles.push(absolutePath);
495
+ const { content: innerContent } = (0, import_gray_matter.default)(fileContent);
496
+ const nested = resolveIncludes(innerContent, absolutePath, {
497
+ maxDepth,
498
+ _depth: _depth + 1
499
+ });
500
+ includedFiles.push(...nested.includedFiles);
501
+ return nested.content;
502
+ }
503
+ );
504
+ return { content: resolved, includedFiles };
505
+ }
472
506
  function myRemarkPlugin() {
473
507
  return (tree) => {
474
508
  (0, import_unist_util_visit.visit)(tree, (node) => {
@@ -487,12 +521,19 @@ function myRemarkPlugin() {
487
521
  }
488
522
  async function transformSlidesMdxToReact(sourceContent, {
489
523
  production: isProd,
524
+ filePath,
490
525
  ...options
491
526
  }) {
492
527
  const { data: metadata, content } = (0, import_gray_matter.default)(sourceContent);
493
- const { content: finalContent, inlineModules } = extractInlineModules(
494
- normalizeNewline(content)
495
- );
528
+ const normalized = normalizeNewline(content);
529
+ let resolvedContent = normalized;
530
+ let includedFiles = [];
531
+ if (filePath) {
532
+ const result = resolveIncludes(normalized, filePath);
533
+ resolvedContent = result.content;
534
+ includedFiles = result.includedFiles;
535
+ }
536
+ const { content: finalContent, inlineModules } = extractInlineModules(resolvedContent);
496
537
  const slides = finalContent.split("---\n");
497
538
  const enrichedSlides = [];
498
539
  let frontmatterForNextSlide = null;
@@ -564,7 +605,7 @@ Deck.slides = [
564
605
  inlineModules
565
606
  );
566
607
  import_node_fs.default.writeFileSync("slides.js", output);
567
- return output;
608
+ return { code: output, includedFiles };
568
609
  }
569
610
  var MOD_REG = /\\`|`(?:\\`|[^`])*`|(^(?:import|export).*$)/gm;
570
611
  function extractInlineModules(source, {
@@ -607,9 +648,9 @@ var PestacleConfigSchema = import_zod.z.object({
607
648
  });
608
649
 
609
650
  // src/index.ts
610
- async function checkIfDirectoryExists(path) {
651
+ async function checkIfDirectoryExists(path2) {
611
652
  try {
612
- await fs2.access(path);
653
+ await fs2.access(path2);
613
654
  return true;
614
655
  } catch {
615
656
  return false;
@@ -653,10 +694,10 @@ async function extractDeckMeta(filePath) {
653
694
  return { slideCount: 0 };
654
695
  }
655
696
  }
656
- async function fileExists(_name, path) {
697
+ async function fileExists(_name, path2) {
657
698
  const candidateExts = [".ts", ".tsx", ".js", ".jsx"];
658
699
  for await (const ext of candidateExts) {
659
- const fullPath = `${path}${ext}`;
700
+ const fullPath = `${path2}${ext}`;
660
701
  try {
661
702
  await fs2.access(fullPath);
662
703
  return fullPath.replace(/^\./, "");
@@ -765,9 +806,9 @@ var src_default = async (options) => {
765
806
  let deckTheme;
766
807
  try {
767
808
  const raw = await fs2.readFile(deckMdxPath, "utf-8");
768
- const { data: data2 } = (0, import_gray_matter2.default)(raw);
769
- if (typeof data2.theme === "string") {
770
- deckTheme = data2.theme;
809
+ const { data } = (0, import_gray_matter2.default)(raw);
810
+ if (typeof data.theme === "string") {
811
+ deckTheme = data.theme;
771
812
  }
772
813
  } catch {
773
814
  }
@@ -782,12 +823,17 @@ var src_default = async (options) => {
782
823
  if (!id.endsWith("deck.mdx")) {
783
824
  return;
784
825
  }
826
+ const absoluteId = id.startsWith("/") ? id : `${process.cwd()}/${id}`;
785
827
  const content = await fs2.readFile(id, "utf-8");
786
- const data = await transformSlidesMdxToReact(content, {
828
+ const { code, includedFiles } = await transformSlidesMdxToReact(content, {
787
829
  production: isProd,
830
+ filePath: absoluteId,
788
831
  ...options
789
832
  });
790
- return data;
833
+ for (const file of includedFiles) {
834
+ this.addWatchFile(file);
835
+ }
836
+ return code;
791
837
  },
792
838
  transformIndexHtml: {
793
839
  order: "pre",
package/index.mjs CHANGED
@@ -406,6 +406,7 @@ document.head.appendChild(link)
406
406
 
407
407
  // src/slides.ts
408
408
  import fs from "fs";
409
+ import path from "path";
409
410
  import { compile } from "@mdx-js/mdx";
410
411
  import matter from "gray-matter";
411
412
  import remarkDirective from "remark-directive";
@@ -448,6 +449,39 @@ ${header}
448
449
  }
449
450
 
450
451
  // src/slides.ts
452
+ var INCLUDE_RE = /^::include\{file=(.+?)\}\s*$/gm;
453
+ function resolveIncludes(content, filePath, { maxDepth = 10, _depth = 0 } = {}) {
454
+ const includedFiles = [];
455
+ const dir = path.dirname(filePath);
456
+ const resolved = content.replace(
457
+ INCLUDE_RE,
458
+ (_match, includePath) => {
459
+ if (_depth >= maxDepth) {
460
+ throw new Error(
461
+ `Include depth limit (${maxDepth}) exceeded when including "${includePath}" from "${filePath}". Check for circular includes.`
462
+ );
463
+ }
464
+ const absolutePath = path.resolve(dir, includePath);
465
+ let fileContent;
466
+ try {
467
+ fileContent = fs.readFileSync(absolutePath, "utf-8");
468
+ } catch {
469
+ throw new Error(
470
+ `Could not read included file "${includePath}" (resolved to "${absolutePath}") from "${filePath}".`
471
+ );
472
+ }
473
+ includedFiles.push(absolutePath);
474
+ const { content: innerContent } = matter(fileContent);
475
+ const nested = resolveIncludes(innerContent, absolutePath, {
476
+ maxDepth,
477
+ _depth: _depth + 1
478
+ });
479
+ includedFiles.push(...nested.includedFiles);
480
+ return nested.content;
481
+ }
482
+ );
483
+ return { content: resolved, includedFiles };
484
+ }
451
485
  function myRemarkPlugin() {
452
486
  return (tree) => {
453
487
  visit(tree, (node) => {
@@ -466,12 +500,19 @@ function myRemarkPlugin() {
466
500
  }
467
501
  async function transformSlidesMdxToReact(sourceContent, {
468
502
  production: isProd,
503
+ filePath,
469
504
  ...options
470
505
  }) {
471
506
  const { data: metadata, content } = matter(sourceContent);
472
- const { content: finalContent, inlineModules } = extractInlineModules(
473
- normalizeNewline(content)
474
- );
507
+ const normalized = normalizeNewline(content);
508
+ let resolvedContent = normalized;
509
+ let includedFiles = [];
510
+ if (filePath) {
511
+ const result = resolveIncludes(normalized, filePath);
512
+ resolvedContent = result.content;
513
+ includedFiles = result.includedFiles;
514
+ }
515
+ const { content: finalContent, inlineModules } = extractInlineModules(resolvedContent);
475
516
  const slides = finalContent.split("---\n");
476
517
  const enrichedSlides = [];
477
518
  let frontmatterForNextSlide = null;
@@ -543,7 +584,7 @@ Deck.slides = [
543
584
  inlineModules
544
585
  );
545
586
  fs.writeFileSync("slides.js", output);
546
- return output;
587
+ return { code: output, includedFiles };
547
588
  }
548
589
  var MOD_REG = /\\`|`(?:\\`|[^`])*`|(^(?:import|export).*$)/gm;
549
590
  function extractInlineModules(source, {
@@ -591,9 +632,9 @@ function defineConfig(config) {
591
632
  }
592
633
 
593
634
  // src/index.ts
594
- async function checkIfDirectoryExists(path) {
635
+ async function checkIfDirectoryExists(path2) {
595
636
  try {
596
- await fs2.access(path);
637
+ await fs2.access(path2);
597
638
  return true;
598
639
  } catch {
599
640
  return false;
@@ -637,10 +678,10 @@ async function extractDeckMeta(filePath) {
637
678
  return { slideCount: 0 };
638
679
  }
639
680
  }
640
- async function fileExists(_name, path) {
681
+ async function fileExists(_name, path2) {
641
682
  const candidateExts = [".ts", ".tsx", ".js", ".jsx"];
642
683
  for await (const ext of candidateExts) {
643
- const fullPath = `${path}${ext}`;
684
+ const fullPath = `${path2}${ext}`;
644
685
  try {
645
686
  await fs2.access(fullPath);
646
687
  return fullPath.replace(/^\./, "");
@@ -749,9 +790,9 @@ var index_default = async (options) => {
749
790
  let deckTheme;
750
791
  try {
751
792
  const raw = await fs2.readFile(deckMdxPath, "utf-8");
752
- const { data: data2 } = matter2(raw);
753
- if (typeof data2.theme === "string") {
754
- deckTheme = data2.theme;
793
+ const { data } = matter2(raw);
794
+ if (typeof data.theme === "string") {
795
+ deckTheme = data.theme;
755
796
  }
756
797
  } catch {
757
798
  }
@@ -766,12 +807,17 @@ var index_default = async (options) => {
766
807
  if (!id.endsWith("deck.mdx")) {
767
808
  return;
768
809
  }
810
+ const absoluteId = id.startsWith("/") ? id : `${process.cwd()}/${id}`;
769
811
  const content = await fs2.readFile(id, "utf-8");
770
- const data = await transformSlidesMdxToReact(content, {
812
+ const { code, includedFiles } = await transformSlidesMdxToReact(content, {
771
813
  production: isProd,
814
+ filePath: absoluteId,
772
815
  ...options
773
816
  });
774
- return data;
817
+ for (const file of includedFiles) {
818
+ this.addWatchFile(file);
819
+ }
820
+ return code;
775
821
  },
776
822
  transformIndexHtml: {
777
823
  order: "pre",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-react-deck",
3
- "version": "1.5.2",
3
+ "version": "1.6.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "index.cjs",
package/slides.d.ts CHANGED
@@ -1,5 +1,18 @@
1
1
  import type { ReactDeckOptions } from "./types";
2
2
  type CompileOptions = Pick<ReactDeckOptions, "rehypePlugins" | "remarkPlugins">;
3
+ /**
4
+ * Resolve `::include{file=./path/to/file.mdx}` directives by replacing them
5
+ * with the content of the referenced file (stripping its top-level frontmatter).
6
+ * Supports nested includes up to a depth limit to prevent infinite recursion.
7
+ * Returns the resolved content and the set of included file paths (for HMR).
8
+ */
9
+ export declare function resolveIncludes(content: string, filePath: string, { maxDepth, _depth }?: {
10
+ maxDepth?: number;
11
+ _depth?: number;
12
+ }): {
13
+ content: string;
14
+ includedFiles: string[];
15
+ };
3
16
  /**
4
17
  * Slides are separated by "---" in the markdown file.
5
18
  *
@@ -23,9 +36,14 @@ type CompileOptions = Pick<ReactDeckOptions, "rehypePlugins" | "remarkPlugins">;
23
36
  * # Slide 2
24
37
  *
25
38
  */
26
- export declare function transformSlidesMdxToReact(sourceContent: string, { production: isProd, ...options }: {
39
+ export declare function transformSlidesMdxToReact(sourceContent: string, { production: isProd, filePath, ...options }: {
27
40
  production: boolean;
28
- } & CompileOptions): Promise<string>;
41
+ /** Absolute path to the source MDX file, used to resolve includes. */
42
+ filePath?: string;
43
+ } & CompileOptions): Promise<{
44
+ code: string;
45
+ includedFiles: string[];
46
+ }>;
29
47
  export declare function extractInlineModules(source: string, { inlineModules, }?: {
30
48
  inlineModules?: Set<string>;
31
49
  }): {