@void/md 1.5.0 → 1.6.1

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.
@@ -1,5 +1,4 @@
1
- import { t as extractHeadings } from "./metadata-BoVkSwbJ.mjs";
2
- import matter from "gray-matter";
1
+ import { r as parseFrontmatter, t as extractHeadings } from "./metadata-DDtNQG1v.mjs";
3
2
  import MarkdownExit from "markdown-exit";
4
3
  import anchor from "markdown-it-anchor";
5
4
  import footnote from "markdown-it-footnote";
@@ -289,7 +288,7 @@ async function createCompiler(options) {
289
288
  return markdown;
290
289
  }
291
290
  async function compile(source, options) {
292
- const { data: frontmatter, content } = matter(source);
291
+ const { frontmatter, content } = parseFrontmatter(source);
293
292
  const compiler = await getCompiler(options);
294
293
  const env = {};
295
294
  const tokens = compiler.parse(content, env);
@@ -32,8 +32,20 @@ function extractHeadings(tokens) {
32
32
  }
33
33
  return headings;
34
34
  }
35
+ function parseFrontmatter(source) {
36
+ try {
37
+ const { data: frontmatter, content } = matter(source, {});
38
+ return {
39
+ frontmatter,
40
+ content
41
+ };
42
+ } catch (error) {
43
+ const message = error instanceof Error ? error.message : String(error);
44
+ throw new Error(`Invalid frontmatter: ${message}`, { cause: error });
45
+ }
46
+ }
35
47
  function extractMetadata(source) {
36
- const { data: frontmatter, content } = matter(source);
48
+ const { frontmatter, content } = parseFrontmatter(source);
37
49
  const headings = extractHeadings(getMetadataCompiler().parse(content, {}));
38
50
  return {
39
51
  frontmatter,
@@ -41,4 +53,4 @@ function extractMetadata(source) {
41
53
  title: frontmatter.title ?? headings.find((heading) => heading.depth === 1)?.text ?? ""
42
54
  };
43
55
  }
44
- export { extractMetadata as n, extractHeadings as t };
56
+ export { extractMetadata as n, parseFrontmatter as r, extractHeadings as t };
package/dist/plugin.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { n as extractMetadata } from "./metadata-BoVkSwbJ.mjs";
1
+ import { n as extractMetadata } from "./metadata-DDtNQG1v.mjs";
2
2
  import { createHash } from "node:crypto";
3
- import { existsSync, readFileSync, readdirSync } from "node:fs";
3
+ import { existsSync, readFileSync } from "node:fs";
4
4
  import { dirname, join, relative, resolve } from "pathslash";
5
5
  import { glob } from "tinyglobby";
6
6
  import { normalizePath } from "vite";
@@ -299,26 +299,16 @@ function mdRouteName(file, env) {
299
299
  if (name === "index") return "";
300
300
  return name.endsWith("/index") ? name.slice(0, -6) : name;
301
301
  }
302
- function hasMdFilesWithScript(dir) {
303
- try {
304
- for (const entry of readdirSync(dir, {
305
- withFileTypes: true,
306
- recursive: true
307
- })) if (entry.isFile() && entry.name.endsWith(".md")) {
308
- const fullPath = join(entry.parentPath, entry.name);
309
- const content = readFileSync(fullPath, "utf-8");
310
- if (/^<script\b/m.test(content)) return true;
311
- }
312
- } catch {}
313
- return false;
302
+ /** The Markdown files that `environment` keeps, relative to `pagesDir`. */
303
+ async function listMdFiles(pagesDir, environment) {
304
+ return (await glob("**/*.md", { cwd: pagesDir })).filter((file) => {
305
+ const fileEnv = pageFileEnvironment(file);
306
+ return !fileEnv || fileEnv === environment;
307
+ });
314
308
  }
315
- function hasIslandFiles(dir) {
316
- try {
317
- for (const entry of readdirSync(dir, {
318
- withFileTypes: true,
319
- recursive: true
320
- })) if (entry.isFile() && /\.island\./.test(entry.name)) return true;
321
- } catch {}
309
+ /** Whether any page has client code. Without it, pages would load an empty client entry. */
310
+ async function hasMdClientCode(pagesDir, environment) {
311
+ for (const file of await listMdFiles(pagesDir, environment)) if (extractScript(readFileSync(join(pagesDir, file), "utf-8")).clientCode) return true;
322
312
  return false;
323
313
  }
324
314
  function voidMarkdown(options) {
@@ -326,31 +316,51 @@ function voidMarkdown(options) {
326
316
  let mdPages = [];
327
317
  let root = "";
328
318
  let pagesDir = "";
319
+ let logger;
329
320
  let mdEnvironment = "prod";
330
321
  const svelteModules = /* @__PURE__ */ new Map();
331
322
  const solidModules = /* @__PURE__ */ new Map();
332
323
  const clientScripts = /* @__PURE__ */ new Map();
324
+ const loggedPageErrors = /* @__PURE__ */ new Map();
333
325
  function createVirtualModuleId(prefix, importerId, source, extension) {
334
326
  return `${prefix}${Buffer.from(importerId).toString("base64url")}.${createHash("sha1").update(source).digest("hex").slice(0, 8)}.${extension}`;
335
327
  }
328
+ function createPageError(file, error) {
329
+ const message = error instanceof Error ? error.message : String(error);
330
+ return new Error(`void-md: ${relative(root, file)}: ${message}`, { cause: error });
331
+ }
336
332
  async function scanMdPages() {
337
333
  if (!existsSync(pagesDir)) return [];
338
- const liveFiles = (await glob("**/*.md", { cwd: pagesDir })).filter((file) => {
339
- const fileEnv = pageFileEnvironment(file);
340
- return !fileEnv || fileEnv === mdEnvironment;
341
- });
334
+ const liveFiles = await listMdFiles(pagesDir, mdEnvironment);
342
335
  return (await Promise.all(liveFiles.map(async (file) => {
343
- const { body, clientCode } = extractScript(readFileSync(join(pagesDir, file), "utf-8"));
344
- const result = extractMetadata(body);
345
- const componentId = stripPageSuffix(file) || "index";
346
- if (clientCode) clientScripts.set(componentId, clientCode);
347
- return {
348
- path: "/" + mdRouteName(file, pageFileEnvironment(file)),
349
- title: result.title,
350
- frontmatter: result.frontmatter,
351
- headings: result.headings
352
- };
353
- }))).sort((leftPage, rightPage) => leftPage.path.localeCompare(rightPage.path));
336
+ const path = "/" + mdRouteName(file, pageFileEnvironment(file));
337
+ const filePath = join(pagesDir, file);
338
+ try {
339
+ const { body, clientCode } = extractScript(readFileSync(filePath, "utf-8"));
340
+ const result = extractMetadata(body);
341
+ const componentId = stripPageSuffix(file) || "index";
342
+ if (clientCode) clientScripts.set(componentId, clientCode);
343
+ loggedPageErrors.delete(file);
344
+ return {
345
+ path,
346
+ title: result.title,
347
+ frontmatter: result.frontmatter,
348
+ headings: result.headings
349
+ };
350
+ } catch (error) {
351
+ const pageError = createPageError(filePath, error);
352
+ if (mdEnvironment === "prod") throw pageError;
353
+ if (error.code === "ENOENT") return;
354
+ if (loggedPageErrors.get(file) !== pageError.message) {
355
+ loggedPageErrors.set(file, pageError.message);
356
+ logger?.error(pageError.message, {
357
+ error: pageError,
358
+ timestamp: true
359
+ });
360
+ }
361
+ return mdPages.find((page) => page.path === path);
362
+ }
363
+ }))).filter((page) => page !== void 0).sort((leftPage, rightPage) => leftPage.path.localeCompare(rightPage.path));
354
364
  }
355
365
  return [{
356
366
  name: "void-md",
@@ -361,16 +371,17 @@ function voidMarkdown(options) {
361
371
  return clientScripts;
362
372
  }
363
373
  },
364
- config(config, env) {
374
+ async config(config, env) {
365
375
  mdEnvironment = env.command === "serve" && !env.isPreview ? "dev" : "prod";
366
376
  if (env.command === "serve") return;
367
377
  const configRoot = config.root || process.cwd();
368
378
  const pagesDir = resolvePagesDir(configRoot);
369
379
  if (!existsSync(pagesDir)) return;
370
- if (hasMdFilesWithScript(pagesDir) && hasIslandFiles(pagesDir)) return { environments: { client: { build: { rollupOptions: { input: { "md-client": MD_CLIENT_ENTRY_ID } } } } } };
380
+ if (await hasMdClientCode(pagesDir, mdEnvironment)) return { environments: { client: { build: { rollupOptions: { input: { "md-client": MD_CLIENT_ENTRY_ID } } } } } };
371
381
  },
372
382
  configResolved(config) {
373
383
  root = config.root;
384
+ logger = config.logger;
374
385
  pagesDir = resolvePagesDir(root);
375
386
  for (const plugin of config.plugins) {
376
387
  const match = ADAPTER_PLUGIN_NAMES[plugin.name];
@@ -392,7 +403,15 @@ function voidMarkdown(options) {
392
403
  const isInPagesDir = (file) => file === normalizedPagesDir || file.startsWith(normalizedPagesDir + "/");
393
404
  const handleMdChange = async (file) => {
394
405
  if (!file.endsWith(".md") || !isInPagesDir(normalizePath(file))) return;
395
- mdPages = await scanMdPages();
406
+ try {
407
+ mdPages = await scanMdPages();
408
+ } catch (error) {
409
+ server.config.logger.error(error instanceof Error ? error.message : String(error), {
410
+ error: error instanceof Error ? error : void 0,
411
+ timestamp: true
412
+ });
413
+ return;
414
+ }
396
415
  const mod = server.moduleGraph.getModuleById("\0void-md-pages");
397
416
  if (mod) server.moduleGraph.invalidateModule(mod);
398
417
  for (const [compId] of clientScripts) {
@@ -481,8 +500,10 @@ export function useFrontmatter() { return useContext(FrontmatterContext) ?? {};
481
500
  islandId
482
501
  };
483
502
  });
484
- const { compile } = await import("./compile-BCD-iOV3.mjs");
485
- const result = await compile(body, { shiki: options?.shiki });
503
+ const { compile } = await import("./compile-5IQ1Trxe.mjs");
504
+ const result = await compile(body, { shiki: options?.shiki }).catch((error) => {
505
+ throw createPageError(_id, error);
506
+ });
486
507
  if (adapter === "vue") return {
487
508
  code: emitVueComponent(result, islands),
488
509
  map: null
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@void/md",
3
- "version": "1.5.0",
3
+ "version": "1.6.1",
4
4
  "description": "Markdown pages with layouts and interactive islands for Void.",
5
5
  "homepage": "https://void.cloud",
6
6
  "bugs": {
@@ -54,11 +54,11 @@
54
54
  "@types/node": "^26.6.6",
55
55
  "vite": "npm:@voidzero-dev/vite-plus-core@1.4.0",
56
56
  "vite-plus": "1.4.0",
57
- "void": "1.5.0"
57
+ "void": "1.6.1"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "vite": "^8.0.0",
61
- "void": "1.5.0"
61
+ "void": "1.6.1"
62
62
  },
63
63
  "scripts": {
64
64
  "build": "vp pack",