seemore 1.12.13 → 1.12.14

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/dist/cli/index.js CHANGED
@@ -78,7 +78,7 @@ import pc5 from "picocolors";
78
78
  import { mkdirSync as mkdirSync3, mkdtempSync, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync7 } from "fs";
79
79
  import { createRequire as createRequire4 } from "module";
80
80
  import { tmpdir as tmpdir3 } from "os";
81
- import { isAbsolute as isAbsolute2, join as join12, relative as relative3, resolve as resolve5 } from "path";
81
+ import { isAbsolute as isAbsolute2, join as join12, relative as relative4, resolve as resolve6 } from "path";
82
82
  import pc2 from "picocolors";
83
83
  import { build as viteBuild } from "vite";
84
84
 
@@ -961,9 +961,9 @@ function splitHash(value) {
961
961
  if (index === -1) return [value, void 0];
962
962
  return [value.slice(0, index), value.slice(index + 1)];
963
963
  }
964
- function joinPosix(fromDir, relative5) {
964
+ function joinPosix(fromDir, relative6) {
965
965
  const segments = [...fromDir];
966
- for (const part of toPosix(relative5).split("/")) {
966
+ for (const part of toPosix(relative6).split("/")) {
967
967
  if (part === "" || part === ".") continue;
968
968
  if (part === "..") segments.pop();
969
969
  else segments.push(part);
@@ -1469,6 +1469,164 @@ function rehypeSeemoreRawHtml() {
1469
1469
  };
1470
1470
  }
1471
1471
 
1472
+ // src/node/vite/media.ts
1473
+ import { existsSync as existsSync4 } from "fs";
1474
+ import { dirname as dirname7, relative as relative2, resolve as resolve5 } from "path";
1475
+ import { visit as visit3 } from "unist-util-visit";
1476
+ var MEDIA = {
1477
+ video: ["src", "poster"],
1478
+ audio: ["src"],
1479
+ source: ["src"],
1480
+ track: ["src"]
1481
+ };
1482
+ function rehypeSeemoreMedia(options) {
1483
+ return (tree, file) => {
1484
+ const path = file.path ?? "";
1485
+ if (path === "") return;
1486
+ const dir = dirname7(path.split("?")[0] ?? path);
1487
+ const from = toPosix(relative2(options.contentRoot, path.split("?")[0] ?? path));
1488
+ const imports = /* @__PURE__ */ new Map();
1489
+ const importFor = (url) => {
1490
+ if (/^(?:[a-z][a-z0-9+.-]*:|\/\/|\/|#)/i.test(url) || url === "") return void 0;
1491
+ const raw = url.split(/[?#]/)[0] ?? "";
1492
+ let decoded;
1493
+ try {
1494
+ decoded = decodeURIComponent(raw);
1495
+ } catch {
1496
+ decoded = raw;
1497
+ }
1498
+ if (!existsSync4(resolve5(dir, decoded))) {
1499
+ options.onWarning(`Missing asset ${url} referenced by ${from}.`);
1500
+ return void 0;
1501
+ }
1502
+ const specifier = /^\.{1,2}\//.test(decoded) ? decoded : `./${decoded}`;
1503
+ let name = imports.get(specifier);
1504
+ if (name === void 0) {
1505
+ name = `__seemoreMedia${imports.size}`;
1506
+ imports.set(specifier, name);
1507
+ }
1508
+ return name;
1509
+ };
1510
+ visit3(tree, (node, index, parent) => {
1511
+ if (parent === void 0 || index === void 0) return;
1512
+ if (node.type === "element") {
1513
+ const fields = MEDIA[node.tagName];
1514
+ if (fields === void 0) return;
1515
+ const bound = /* @__PURE__ */ new Map();
1516
+ for (const field of fields) {
1517
+ const value = node.properties[field];
1518
+ if (typeof value !== "string") continue;
1519
+ const name = importFor(value);
1520
+ if (name !== void 0) bound.set(field, name);
1521
+ }
1522
+ if (bound.size === 0) return;
1523
+ parent.children[index] = toJsx(node, bound);
1524
+ return;
1525
+ }
1526
+ if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") {
1527
+ const element = node;
1528
+ const fields = element.name === null ? void 0 : MEDIA[element.name];
1529
+ if (fields === void 0) return;
1530
+ for (const attribute of element.attributes) {
1531
+ if (attribute.type !== "mdxJsxAttribute" || !fields.includes(attribute.name)) continue;
1532
+ if (typeof attribute.value !== "string") continue;
1533
+ const name = importFor(attribute.value);
1534
+ if (name !== void 0) attribute.value = identifierValue(name);
1535
+ }
1536
+ }
1537
+ });
1538
+ if (imports.size === 0) return;
1539
+ tree.children.unshift(
1540
+ ...[...imports].map(([specifier, name]) => importNode(specifier, name))
1541
+ );
1542
+ };
1543
+ }
1544
+ function toJsx(node, bound) {
1545
+ const attributes = [];
1546
+ for (const [key, value] of Object.entries(node.properties)) {
1547
+ if (value === void 0 || value === null || value === false) continue;
1548
+ const name = attributeName(key);
1549
+ const imported = bound.get(key);
1550
+ if (imported !== void 0) {
1551
+ attributes.push({ type: "mdxJsxAttribute", name, value: identifierValue(imported) });
1552
+ } else if (key === "style" && typeof value === "string") {
1553
+ attributes.push({ type: "mdxJsxAttribute", name, value: styleValue(value) });
1554
+ } else if (value === true) {
1555
+ attributes.push({ type: "mdxJsxAttribute", name, value: null });
1556
+ } else {
1557
+ attributes.push({ type: "mdxJsxAttribute", name, value: Array.isArray(value) ? value.join(" ") : String(value) });
1558
+ }
1559
+ }
1560
+ return {
1561
+ type: node.children.some((child) => child.type === "element" && child.tagName === "p") ? "mdxJsxFlowElement" : "mdxJsxTextElement",
1562
+ name: node.tagName,
1563
+ attributes,
1564
+ children: node.children
1565
+ };
1566
+ }
1567
+ function attributeName(key) {
1568
+ const match = /^(data|aria)([A-Z].*)$/.exec(key);
1569
+ if (match === null) return key;
1570
+ return `${match[1]}${(match[2] ?? "").replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}`;
1571
+ }
1572
+ function identifierValue(name) {
1573
+ return expressionValue(name, { type: "Identifier", name });
1574
+ }
1575
+ function styleValue(css) {
1576
+ const properties = [];
1577
+ for (const declaration of css.split(";")) {
1578
+ const colon = declaration.indexOf(":");
1579
+ if (colon === -1) continue;
1580
+ const property = declaration.slice(0, colon).trim();
1581
+ const value = declaration.slice(colon + 1).trim();
1582
+ if (property === "" || value === "") continue;
1583
+ const key = property.startsWith("--") ? property : property.replace(/^-ms-/, "ms-").replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
1584
+ properties.push({
1585
+ type: "Property",
1586
+ kind: "init",
1587
+ method: false,
1588
+ shorthand: false,
1589
+ computed: false,
1590
+ key: { type: "Literal", value: key },
1591
+ value: { type: "Literal", value }
1592
+ });
1593
+ }
1594
+ return expressionValue(JSON.stringify(css), { type: "ObjectExpression", properties });
1595
+ }
1596
+ function expressionValue(source, expression) {
1597
+ return {
1598
+ type: "mdxJsxAttributeValueExpression",
1599
+ value: source,
1600
+ data: {
1601
+ estree: {
1602
+ type: "Program",
1603
+ sourceType: "module",
1604
+ body: [{ type: "ExpressionStatement", expression }]
1605
+ }
1606
+ }
1607
+ };
1608
+ }
1609
+ function importNode(specifier, name) {
1610
+ return {
1611
+ type: "mdxjsEsm",
1612
+ value: "",
1613
+ data: {
1614
+ estree: {
1615
+ type: "Program",
1616
+ sourceType: "module",
1617
+ body: [
1618
+ {
1619
+ type: "ImportDeclaration",
1620
+ attributes: [],
1621
+ specifiers: [{ type: "ImportDefaultSpecifier", local: { type: "Identifier", name } }],
1622
+ source: { type: "Literal", value: specifier }
1623
+ }
1624
+ ]
1625
+ }
1626
+ }
1627
+ };
1628
+ }
1629
+
1472
1630
  // src/node/vite/mdx.ts
1473
1631
  var MARKDOWN_EXPORT = "_markdown";
1474
1632
  function createRemarkPlugins(options) {
@@ -1510,7 +1668,7 @@ function createRemarkPlugins(options) {
1510
1668
  () => remarkSeemoreLinks(options)
1511
1669
  ];
1512
1670
  }
1513
- function createRehypePlugins(options = {}) {
1671
+ function createRehypePlugins(options) {
1514
1672
  return [
1515
1673
  // A fence in a language Shiki has no grammar for (anything an AI dreamt up) is plain code
1516
1674
  // on the page, not a dead one: `plaintext` is special-cased by Shiki and never needs
@@ -1525,14 +1683,17 @@ function createRehypePlugins(options = {}) {
1525
1683
  // `rehype-code` reads `noCopy` from. Running it here means each plugin above has already
1526
1684
  // taken what it needs, and the elements it creates are not stamped as editable, which is
1527
1685
  // right: they have no source range a text editor could be handed.
1528
- rehypeSeemoreRawHtml
1686
+ rehypeSeemoreRawHtml,
1687
+ // After `rehype-raw`, the first point where a `.md` file's hand-written `<video>` is an
1688
+ // element rather than a string.
1689
+ () => rehypeSeemoreMedia(options)
1529
1690
  ];
1530
1691
  }
1531
1692
 
1532
1693
  // src/node/vite/plugin.ts
1533
1694
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
1534
1695
  import { createRequire as createRequire2 } from "module";
1535
- import { dirname as dirname7 } from "path";
1696
+ import { dirname as dirname8 } from "path";
1536
1697
 
1537
1698
  // src/node/search/build.ts
1538
1699
  import { readFileSync as readFileSync5 } from "fs";
@@ -1628,7 +1789,7 @@ var require_2 = createRequire2(import.meta.url);
1628
1789
  function styleImports(ctx) {
1629
1790
  const lines = [`@import 'fumadocs-ui/css/${ctx.config.theme}.css';`];
1630
1791
  try {
1631
- lines.push(`@source '${dirname7(require_2.resolve("fumadocs-ui/package.json"))}/dist';`);
1792
+ lines.push(`@source '${dirname8(require_2.resolve("fumadocs-ui/package.json"))}/dist';`);
1632
1793
  } catch {
1633
1794
  }
1634
1795
  return lines.join("\n");
@@ -1875,7 +2036,7 @@ function readIfExists(path) {
1875
2036
  }
1876
2037
 
1877
2038
  // src/node/vite/watcher.ts
1878
- import { relative as relative2 } from "path";
2039
+ import { relative as relative3 } from "path";
1879
2040
  import chokidar from "chokidar";
1880
2041
  var CONTENT_FILE = /\.(?:mdx?|json)$/i;
1881
2042
  var EXCLUDED_DIR = /(?:^|\/)(?:node_modules|dist|build|out|vendor|target|venv|deps|Pods|bower_components|\.[^/]+)(?:$|\/)/;
@@ -1906,7 +2067,7 @@ function seemoreWatcherPlugin(ctx) {
1906
2067
  ignoreInitial: true,
1907
2068
  ignored: (path, stats) => {
1908
2069
  if (path === ctx.config.configFile) return false;
1909
- return isWatchIgnored(relative2(ctx.contentRoot, path), stats?.isFile() === true, ctx.config.include);
2070
+ return isWatchIgnored(relative3(ctx.contentRoot, path), stats?.isFile() === true, ctx.config.include);
1910
2071
  }
1911
2072
  });
1912
2073
  const onEvent = (event, path) => {
@@ -1987,7 +2148,11 @@ function createViteConfig({ ctx, mode, outDir, ssrOutDir, auth = false }) {
1987
2148
  getResolver: () => ctx.resolver(),
1988
2149
  onWarning: (message) => ctx.warnings.add(message)
1989
2150
  }),
1990
- rehypePlugins: createRehypePlugins({ positions: mode === "dev" && ctx.config.features["content.edit"] }),
2151
+ rehypePlugins: createRehypePlugins({
2152
+ contentRoot: ctx.contentRoot,
2153
+ onWarning: (message) => ctx.warnings.add(message),
2154
+ positions: mode === "dev" && ctx.config.features["content.edit"]
2155
+ }),
1991
2156
  // MDX compiles its own JSX. Vite's builtin transform infers a file's language from its
1992
2157
  // extension and does not know `.md`/`.mdx`, so leaving JSX in the output would fail to
1993
2158
  // parse. The output is plain JS against the automatic runtime, so nothing downstream has
@@ -2131,7 +2296,7 @@ async function loadPrerenderModule(ctx, ssrOutDir) {
2131
2296
 
2132
2297
  // src/node/social/cards.ts
2133
2298
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync6 } from "fs";
2134
- import { dirname as dirname8, join as join11 } from "path";
2299
+ import { dirname as dirname9, join as join11 } from "path";
2135
2300
 
2136
2301
  // src/shared/og.ts
2137
2302
  function ogImagePath(url) {
@@ -2155,7 +2320,7 @@ async function generateSocialCards(ctx, outDir) {
2155
2320
  const png = await renderCard(takumi, ctx.config.title, page.data.title, page.data.description);
2156
2321
  if (png === void 0) continue;
2157
2322
  const target = join11(outDir, ogImagePath(page.url));
2158
- mkdirSync2(dirname8(target), { recursive: true });
2323
+ mkdirSync2(dirname9(target), { recursive: true });
2159
2324
  writeFileSync6(target, png);
2160
2325
  written++;
2161
2326
  }
@@ -2196,7 +2361,7 @@ async function runBuild(options) {
2196
2361
  const config = { ...loaded.config, base: options.base === void 0 ? loaded.config.base : normaliseBase(options.base) };
2197
2362
  warnAboutMissingBase(config.base, loaded.file);
2198
2363
  const ctx = createContext({ config, contentRoot });
2199
- const outDir = resolve5(options.cwd, options.outDir ?? "dist");
2364
+ const outDir = resolve6(options.cwd, options.outDir ?? "dist");
2200
2365
  assertSafeOutDir(outDir, options.cwd, contentRoot);
2201
2366
  const password = config.auth === void 0 ? void 0 : readPassword("npx seemore build");
2202
2367
  const scan2 = ctx.source.current();
@@ -2205,7 +2370,7 @@ async function runBuild(options) {
2205
2370
  throw new Error(`No Markdown files found under ${contentRoot}. Point seemore at a folder that has some, or check \`exclude\`.`);
2206
2371
  }
2207
2372
  for (const warning of scan2.warnings) ctx.warnings.add(warning);
2208
- console.log(pc2.dim(`seemore ${scan2.pages.length} pages from ${relative3(options.cwd, contentRoot) || "."}`));
2373
+ console.log(pc2.dim(`seemore ${scan2.pages.length} pages from ${relative4(options.cwd, contentRoot) || "."}`));
2209
2374
  await viteBuild(createViteConfig({ ctx, mode: "build", outDir, auth: password !== void 0 }));
2210
2375
  const template = readFileSync7(join12(outDir, "index.html"), "utf8");
2211
2376
  const routes = password === void 0 ? await prerenderPages(ctx, outDir, template) : countRoutes(ctx);
@@ -2224,7 +2389,7 @@ async function runBuild(options) {
2224
2389
  console.log(pc2.dim(`seemore ${encrypted} files encrypted; visitors unlock them with the password`));
2225
2390
  }
2226
2391
  ctx.warnings.flush();
2227
- console.log(pc2.green(`seemore ${routes} pages written to ${relative3(options.cwd, outDir) || outDir}`));
2392
+ console.log(pc2.green(`seemore ${routes} pages written to ${relative4(options.cwd, outDir) || outDir}`));
2228
2393
  return { outDir, routes };
2229
2394
  }
2230
2395
  async function prerenderPages(ctx, outDir, template) {
@@ -2253,7 +2418,7 @@ function countRoutes(ctx) {
2253
2418
  }
2254
2419
  function assertSafeOutDir(outDir, cwd, contentRoot) {
2255
2420
  const contains = (parent, child) => {
2256
- const rel = relative3(parent, child);
2421
+ const rel = relative4(parent, child);
2257
2422
  return rel === "" || !rel.startsWith("..") && !isAbsolute2(rel);
2258
2423
  };
2259
2424
  for (const [name, dir] of [
@@ -2353,8 +2518,8 @@ async function runDev(options) {
2353
2518
  }
2354
2519
 
2355
2520
  // src/cli/export.ts
2356
- import { existsSync as existsSync4, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, readdirSync as readdirSync2, rmSync as rmSync2, statSync, writeFileSync as writeFileSync8, mkdirSync as mkdirSync4 } from "fs";
2357
- import { basename as basename2, dirname as dirname9, extname, join as join13, relative as relative4, resolve as resolve6 } from "path";
2521
+ import { existsSync as existsSync5, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, readdirSync as readdirSync2, rmSync as rmSync2, statSync, writeFileSync as writeFileSync8, mkdirSync as mkdirSync4 } from "fs";
2522
+ import { basename as basename2, dirname as dirname10, extname, join as join13, relative as relative5, resolve as resolve7 } from "path";
2358
2523
  import { tmpdir as tmpdir4 } from "os";
2359
2524
  import pc4 from "picocolors";
2360
2525
  import { build as viteBuild2 } from "vite";
@@ -2389,11 +2554,11 @@ var THEME_INIT = `<script>(function () {
2389
2554
 
2390
2555
  // src/cli/export.ts
2391
2556
  async function runExport(options) {
2392
- const target = resolve6(options.cwd, options.file);
2393
- if (!existsSync4(target) || !statSync(target).isFile()) {
2557
+ const target = resolve7(options.cwd, options.file);
2558
+ if (!existsSync5(target) || !statSync(target).isFile()) {
2394
2559
  throw new Error(`No such file: ${options.file}`);
2395
2560
  }
2396
- const contentRoot = resolveContentRoot(options.cwd, dirname9(target));
2561
+ const contentRoot = resolveContentRoot(options.cwd, dirname10(target));
2397
2562
  const loaded = await loadConfig({ root: contentRoot, configPath: resolveConfigPath(options) });
2398
2563
  const config = {
2399
2564
  ...loaded.config,
@@ -2413,7 +2578,7 @@ ${errors.join("\n\n")}`);
2413
2578
  }
2414
2579
  const page = ctx.pages().find((candidate) => candidate.absPath === canonicalise(target));
2415
2580
  if (page === void 0) {
2416
- throw new Error(`${options.file} is not part of this site \u2014 excluded in the config, or outside ${relative4(options.cwd, contentRoot) || "."}.`);
2581
+ throw new Error(`${options.file} is not part of this site \u2014 excluded in the config, or outside ${relative5(options.cwd, contentRoot) || "."}.`);
2417
2582
  }
2418
2583
  const outDir = mkdtempSync2(join13(tmpdir4(), "seemore-export-"));
2419
2584
  const ssrOutDir = mkdtempSync2(join13(tmpdir4(), "seemore-export-ssr-"));
@@ -2426,14 +2591,14 @@ ${errors.join("\n\n")}`);
2426
2591
  const runtime = await bundleRuntime();
2427
2592
  const html = assemble({ article, css, runtime, config, outDir, contentRoot, template });
2428
2593
  const filename = `${basename2(target).replace(/\.(?:md|mdx)$/i, "")}.html`;
2429
- const targetPath = options.out === void 0 ? join13(dirname9(target), filename) : join13(resolve6(options.cwd, options.out), filename);
2430
- if (existsSync4(targetPath)) {
2431
- console.log(pc4.yellow(`seemore replacing existing ${relative4(options.cwd, targetPath) || targetPath}`));
2594
+ const targetPath = options.out === void 0 ? join13(dirname10(target), filename) : join13(resolve7(options.cwd, options.out), filename);
2595
+ if (existsSync5(targetPath)) {
2596
+ console.log(pc4.yellow(`seemore replacing existing ${relative5(options.cwd, targetPath) || targetPath}`));
2432
2597
  }
2433
- mkdirSync4(dirname9(targetPath), { recursive: true });
2598
+ mkdirSync4(dirname10(targetPath), { recursive: true });
2434
2599
  writeFileSync8(targetPath, html, "utf8");
2435
2600
  ctx.warnings.flush();
2436
- console.log(pc4.green(`seemore wrote ${relative4(options.cwd, targetPath) || targetPath} (${formatBytes2(html.length)})`));
2601
+ console.log(pc4.green(`seemore wrote ${relative5(options.cwd, targetPath) || targetPath} (${formatBytes2(html.length)})`));
2437
2602
  } finally {
2438
2603
  rmSync2(outDir, { recursive: true, force: true });
2439
2604
  rmSync2(ssrOutDir, { recursive: true, force: true });
@@ -2441,7 +2606,7 @@ ${errors.join("\n\n")}`);
2441
2606
  }
2442
2607
  function readBuiltCss(outDir) {
2443
2608
  const assets = join13(outDir, "assets");
2444
- const files = existsSync4(assets) ? readdirSync2(assets).filter((file) => file.endsWith(".css")) : [];
2609
+ const files = existsSync5(assets) ? readdirSync2(assets).filter((file) => file.endsWith(".css")) : [];
2445
2610
  if (files.length === 0) throw new Error("The build produced no stylesheet \u2014 the export would be unstyled.");
2446
2611
  return files.map((file) => readFileSync8(join13(assets, file), "utf8")).join("\n");
2447
2612
  }
@@ -2532,7 +2697,7 @@ function resolveAsset(src, base, outDir, contentRoot) {
2532
2697
  const suffix = path.replace(/^\/+/, "");
2533
2698
  for (const root of [outDir, contentRoot]) {
2534
2699
  const candidate = join13(root, suffix);
2535
- if (existsSync4(candidate) && statSync(candidate).isFile()) return candidate;
2700
+ if (existsSync5(candidate) && statSync(candidate).isFile()) return candidate;
2536
2701
  }
2537
2702
  return void 0;
2538
2703
  }
@@ -2560,7 +2725,7 @@ function dataUriFor(file) {
2560
2725
  function faviconLink(config, contentRoot, template) {
2561
2726
  if (config.favicon !== void 0) {
2562
2727
  const file = join13(contentRoot, config.favicon);
2563
- if (existsSync4(file)) {
2728
+ if (existsSync5(file)) {
2564
2729
  try {
2565
2730
  return `<link rel="icon" href="${dataUriFor(file)}" />`;
2566
2731
  } catch {