@ox-content/vite-plugin 2.6.0 → 2.8.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.
package/dist/index.cjs CHANGED
@@ -9372,6 +9372,7 @@ function resolveTheme(config) {
9372
9372
  header: merged.header ?? defaultTheme.header,
9373
9373
  footer: merged.footer ?? defaultTheme.footer,
9374
9374
  socialLinks: merged.socialLinks ?? defaultTheme.socialLinks,
9375
+ sidebar: merged.sidebar ?? [],
9375
9376
  embed: merged.embed ?? {},
9376
9377
  css: merged.css ?? "",
9377
9378
  js: merged.js ?? ""
@@ -9381,6 +9382,7 @@ function resolveTheme(config) {
9381
9382
  * Converts resolved theme to the format expected by Rust NAPI.
9382
9383
  */
9383
9384
  function themeToNapi(theme) {
9385
+ const socialLinks = socialLinksToNapi(theme.socialLinks);
9384
9386
  return {
9385
9387
  colors: theme.colors.primary ? {
9386
9388
  primary: theme.colors.primary,
@@ -9426,16 +9428,30 @@ function themeToNapi(theme) {
9426
9428
  message: theme.footer.message,
9427
9429
  copyright: theme.footer.copyright
9428
9430
  } : void 0,
9429
- socialLinks: theme.socialLinks.github || theme.socialLinks.twitter || theme.socialLinks.discord ? {
9430
- github: theme.socialLinks.github,
9431
- twitter: theme.socialLinks.twitter,
9432
- discord: theme.socialLinks.discord
9433
- } : void 0,
9431
+ socialLinks,
9434
9432
  embed: Object.keys(theme.embed).length > 0 ? theme.embed : void 0,
9435
9433
  css: theme.css || void 0,
9436
9434
  js: theme.js || void 0
9437
9435
  };
9438
9436
  }
9437
+ function socialLinksToNapi(links) {
9438
+ if (Array.isArray(links)) {
9439
+ const items = links.map((item) => {
9440
+ return {
9441
+ icon: typeof item.icon === "string" ? item.icon : void 0,
9442
+ iconSvg: typeof item.icon === "object" ? item.icon.svg : void 0,
9443
+ link: item.link,
9444
+ ariaLabel: item.ariaLabel
9445
+ };
9446
+ });
9447
+ return items.length > 0 ? { links: items } : void 0;
9448
+ }
9449
+ return links.github || links.twitter || links.discord ? {
9450
+ github: links.github,
9451
+ twitter: links.twitter,
9452
+ discord: links.discord
9453
+ } : void 0;
9454
+ }
9439
9455
  //#endregion
9440
9456
  //#region src/ssg.ts
9441
9457
  /**
@@ -10791,7 +10807,8 @@ function resolveSsgOptions(ssg) {
10791
10807
  extension: ".html",
10792
10808
  clean: false,
10793
10809
  bare: false,
10794
- generateOgImage: false
10810
+ generateOgImage: false,
10811
+ lastUpdated: false
10795
10812
  };
10796
10813
  if (ssg === true || ssg === void 0) return {
10797
10814
  enabled: true,
@@ -10799,6 +10816,7 @@ function resolveSsgOptions(ssg) {
10799
10816
  clean: false,
10800
10817
  bare: false,
10801
10818
  generateOgImage: false,
10819
+ lastUpdated: false,
10802
10820
  theme: resolveTheme(void 0)
10803
10821
  };
10804
10822
  return {
@@ -10809,6 +10827,7 @@ function resolveSsgOptions(ssg) {
10809
10827
  siteName: ssg.siteName,
10810
10828
  ogImage: ssg.ogImage,
10811
10829
  generateOgImage: ssg.generateOgImage ?? false,
10830
+ lastUpdated: ssg.lastUpdated ?? false,
10812
10831
  siteUrl: ssg.siteUrl,
10813
10832
  theme: resolveTheme(ssg.theme)
10814
10833
  };
@@ -10855,20 +10874,24 @@ function generateBareHtmlPage(content, title) {
10855
10874
  /**
10856
10875
  * Generates HTML page with navigation using Rust NAPI bindings.
10857
10876
  */
10858
- async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, theme) {
10877
+ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, theme, locale, availableLocales) {
10859
10878
  const mod = await require_mermaid.importNapiModule();
10860
10879
  const tocForRust = pageData.toc.map((entry) => ({
10861
10880
  depth: entry.depth,
10862
10881
  text: entry.text,
10863
10882
  slug: entry.slug
10864
10883
  }));
10884
+ const toRustNavItem = (item) => ({
10885
+ title: item.title,
10886
+ path: item.path,
10887
+ href: item.href,
10888
+ children: item.children?.map(toRustNavItem),
10889
+ collapsed: item.collapsed
10890
+ });
10865
10891
  const navGroupsForRust = navGroups.map((group) => ({
10866
10892
  title: group.title,
10867
- items: group.items.map((item) => ({
10868
- title: item.title,
10869
- path: item.path,
10870
- href: item.href
10871
- }))
10893
+ collapsed: group.collapsed,
10894
+ items: group.items.map(toRustNavItem)
10872
10895
  }));
10873
10896
  const themeForRust = theme ? themeToNapi(theme) : void 0;
10874
10897
  const entryPageForRust = pageData.entryPage ? {
@@ -10907,13 +10930,20 @@ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, th
10907
10930
  description: pageData.description,
10908
10931
  content: pageData.content,
10909
10932
  toc: tocForRust,
10933
+ lastUpdated: pageData.lastUpdated,
10910
10934
  path: pageData.path,
10911
10935
  entryPage: entryPageForRust
10912
10936
  }, navGroupsForRust, {
10913
10937
  siteName,
10914
10938
  base,
10915
10939
  ogImage,
10916
- theme: themeForRust
10940
+ theme: themeForRust,
10941
+ locale,
10942
+ availableLocales: availableLocales?.map((l) => ({
10943
+ code: l.code,
10944
+ name: l.name,
10945
+ dir: l.dir ?? "ltr"
10946
+ }))
10917
10947
  });
10918
10948
  }
10919
10949
  const SSG_STYLE_BLOCK_RE = /[ \t]*<!-- ox-content:styles:start -->\s*<style>([\s\S]*?)<\/style>\s*<!-- ox-content:styles:end -->/;
@@ -11088,6 +11118,11 @@ function getHref(inputPath, srcDir, base, extension) {
11088
11118
  if (urlPath === "/" || urlPath === "") return `${base}index${extension}`;
11089
11119
  return `${base}${urlPath}/index${extension}`;
11090
11120
  }
11121
+ function getPageLocale(urlPath, i18n) {
11122
+ if (!i18n) return void 0;
11123
+ const firstSegment = urlPath.split("/").filter(Boolean)[0];
11124
+ return i18n.locales.some((l) => l.code === firstSegment) ? firstSegment : i18n.defaultLocale;
11125
+ }
11091
11126
  /**
11092
11127
  * Gets the OG image output path for a given markdown file.
11093
11128
  */
@@ -11194,6 +11229,63 @@ function buildNavItems(markdownFiles, srcDir, base, extension) {
11194
11229
  });
11195
11230
  return result;
11196
11231
  }
11232
+ function isSafeSidebarLink(link) {
11233
+ const trimmed = link.trim();
11234
+ if (trimmed.startsWith("//")) return false;
11235
+ return !/^[a-z][a-z0-9+.-]*:/i.test(trimmed) || /^(https?:|mailto:)/i.test(trimmed);
11236
+ }
11237
+ function sidebarPath(link) {
11238
+ if (!link || !isSafeSidebarLink(link)) return "";
11239
+ if (/^(https?:|mailto:|#)/i.test(link.trim())) return "";
11240
+ const bare = link.trim().split("#", 1)[0].split("?", 1)[0].replace(/^\/+/, "").replace(/\/$/, "").replace(/\.(md|markdown)$/i, "");
11241
+ if (!bare || bare === "index") return "/";
11242
+ return bare.replace(/\/index$/, "");
11243
+ }
11244
+ function sidebarHref(link, base, extension) {
11245
+ if (!link) return "#";
11246
+ const trimmed = link.trim();
11247
+ if (!isSafeSidebarLink(trimmed)) return "#";
11248
+ if (/^(https?:|mailto:|#)/i.test(trimmed)) return trimmed;
11249
+ const hash = trimmed.includes("#") ? `#${trimmed.split("#").slice(1).join("#")}` : "";
11250
+ const withoutExt = trimmed.split("#", 1)[0].replace(/^\/+/, "").replace(/\/$/, "").replace(/\.(md|markdown)$/i, "");
11251
+ return `${base}${!withoutExt || withoutExt === "index" ? "index" : `${withoutExt.replace(/\/index$/, "")}/index`}${extension}${hash}`;
11252
+ }
11253
+ /**
11254
+ * Builds navigation items from an explicit theme sidebar tree.
11255
+ */
11256
+ function buildThemeNavItems(sidebar, base, extension) {
11257
+ const toNavItem = (item) => {
11258
+ const navItem = {
11259
+ title: item.text ?? item.link ?? "Untitled",
11260
+ path: sidebarPath(item.link),
11261
+ href: sidebarHref(item.link, base, extension)
11262
+ };
11263
+ if (item.items?.length) navItem.children = item.items.map(toNavItem);
11264
+ if (item.collapsed !== void 0) navItem.collapsed = item.collapsed;
11265
+ return navItem;
11266
+ };
11267
+ const groups = [];
11268
+ let looseItems = [];
11269
+ const flushLooseItems = () => {
11270
+ if (looseItems.length > 0) {
11271
+ groups.push({
11272
+ title: "Guide",
11273
+ items: looseItems
11274
+ });
11275
+ looseItems = [];
11276
+ }
11277
+ };
11278
+ for (const item of sidebar) if (item.items?.length && !item.link) {
11279
+ flushLooseItems();
11280
+ groups.push({
11281
+ title: item.text ?? "Guide",
11282
+ items: item.items.map(toNavItem),
11283
+ collapsed: item.collapsed
11284
+ });
11285
+ } else looseItems.push(toNavItem(item));
11286
+ flushLooseItems();
11287
+ return groups;
11288
+ }
11197
11289
  /**
11198
11290
  * Builds all markdown files to static HTML.
11199
11291
  */
@@ -11216,7 +11308,7 @@ async function buildSsg(options, root) {
11216
11308
  });
11217
11309
  } catch {}
11218
11310
  const markdownFiles = await collectMarkdownFiles$1(srcDir);
11219
- const navItems = buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension);
11311
+ const navItems = ssgOptions.theme?.sidebar.length ? buildThemeNavItems(ssgOptions.theme.sidebar, base, ssgOptions.extension) : buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension);
11220
11312
  let siteName = ssgOptions.siteName ?? "Documentation";
11221
11313
  if (!ssgOptions.siteName) try {
11222
11314
  const pkgPath = path.join(root, "package.json");
@@ -11228,6 +11320,7 @@ async function buildSsg(options, root) {
11228
11320
  const ogImageUrlMap = /* @__PURE__ */ new Map();
11229
11321
  const shouldGenerateOgImages = (options.ogImage || ssgOptions.generateOgImage) && !ssgOptions.bare;
11230
11322
  const pageResults = [];
11323
+ const napi = ssgOptions.lastUpdated ? await require_mermaid.importNapiModule() : void 0;
11231
11324
  for (const inputPath of markdownFiles) try {
11232
11325
  const result = await transformMarkdown(await fs_promises.readFile(inputPath, "utf-8"), inputPath, options, {
11233
11326
  convertMdLinks: true,
@@ -11255,6 +11348,7 @@ async function buildSsg(options, root) {
11255
11348
  transformedHtml,
11256
11349
  title,
11257
11350
  description,
11351
+ lastUpdated: napi?.getGitLastUpdated(inputPath, root) ?? void 0,
11258
11352
  frontmatter: result.frontmatter,
11259
11353
  toc: result.toc
11260
11354
  });
@@ -11300,7 +11394,7 @@ async function buildSsg(options, root) {
11300
11394
  ogImageUrlMap.clear();
11301
11395
  }
11302
11396
  for (const pageResult of pageResults) try {
11303
- const { inputPath, transformedHtml, title, description, frontmatter, toc } = pageResult;
11397
+ const { inputPath, transformedHtml, title, description, lastUpdated, frontmatter, toc } = pageResult;
11304
11398
  let pageOgImage = ssgOptions.ogImage;
11305
11399
  if (shouldGenerateOgImages && ogImageUrlMap.has(inputPath)) pageOgImage = ogImageUrlMap.get(inputPath);
11306
11400
  let entryPage;
@@ -11310,16 +11404,20 @@ async function buildSsg(options, root) {
11310
11404
  };
11311
11405
  let html;
11312
11406
  if (ssgOptions.bare) html = generateBareHtmlPage(transformedHtml, title);
11313
- else html = await generateHtmlPage({
11314
- title,
11315
- description,
11316
- content: transformedHtml,
11317
- toc,
11318
- frontmatter,
11319
- path: getUrlPath$1(inputPath, srcDir),
11320
- href: getHref(inputPath, srcDir, base, ssgOptions.extension),
11321
- entryPage
11322
- }, navItems, siteName, base, pageOgImage, ssgOptions.theme);
11407
+ else {
11408
+ const pageData = {
11409
+ title,
11410
+ description,
11411
+ content: transformedHtml,
11412
+ toc,
11413
+ lastUpdated,
11414
+ frontmatter,
11415
+ path: getUrlPath$1(inputPath, srcDir),
11416
+ href: getHref(inputPath, srcDir, base, ssgOptions.extension),
11417
+ entryPage
11418
+ };
11419
+ html = await generateHtmlPage(pageData, navItems, siteName, base, pageOgImage, ssgOptions.theme, getPageLocale(pageData.path, options.i18n), options.i18n ? options.i18n.locales : void 0);
11420
+ }
11323
11421
  const outputPath = getOutputPath(inputPath, srcDir, outDir, ssgOptions.extension);
11324
11422
  generatedPages.push({
11325
11423
  inputPath,
@@ -12319,7 +12417,7 @@ function createI18nPlugin(resolvedOptions) {
12319
12417
  }
12320
12418
  server.middlewares.use((req, _res, next) => {
12321
12419
  if (!req.url) return next();
12322
- const localeMatch = req.url.match(/^\/([a-z]{2}(?:-[a-zA-Z]+)?)(\/|$)/);
12420
+ const localeMatch = req.url.match(/^\/([A-Za-z]{2,3}(?:-[A-Za-z0-9]+)*)(\/|$)/);
12323
12421
  if (localeMatch) {
12324
12422
  const localeCode = localeMatch[1];
12325
12423
  if (i18nOptions.locales.some((l) => l.code === localeCode)) req.__oxLocale = localeCode;
@@ -12367,14 +12465,14 @@ export function t(key, params, locale) {
12367
12465
  }
12368
12466
  if (params) {
12369
12467
  for (const [k, v] of Object.entries(params)) {
12370
- message = message.replace(new RegExp('\\\\{\\\\$' + k + '\\\\}', 'g'), String(v));
12468
+ message = message.split('{$' + k + '}').join(String(v));
12371
12469
  }
12372
12470
  }
12373
12471
  return message;
12374
12472
  }
12375
12473
 
12376
12474
  export function getLocaleFromPath(pathname) {
12377
- const match = pathname.match(/^\\/([a-z]{2}(?:-[a-zA-Z]+)?)(\\//|$)/);
12475
+ const match = pathname.match(new RegExp('^/([A-Za-z]{2,3}(?:-[A-Za-z0-9]+)*)(/|$)'));
12378
12476
  if (match) {
12379
12477
  const code = match[1];
12380
12478
  if (i18nConfig.locales.some(l => l.code === code)) {
@@ -12388,7 +12486,9 @@ export function localePath(pathname, locale) {
12388
12486
  const current = getLocaleFromPath(pathname);
12389
12487
  let clean = pathname;
12390
12488
  if (current !== i18nConfig.defaultLocale || !i18nConfig.hideDefaultLocale) {
12391
- clean = pathname.replace(new RegExp('^/' + current + '(/|$)'), '/');
12489
+ const prefix = '/' + current;
12490
+ if (clean === prefix) clean = '/';
12491
+ else if (clean.startsWith(prefix + '/')) clean = clean.slice(prefix.length);
12392
12492
  }
12393
12493
  if (locale === i18nConfig.defaultLocale && i18nConfig.hideDefaultLocale) {
12394
12494
  return clean || '/';
@@ -12396,8 +12496,95 @@ export function localePath(pathname, locale) {
12396
12496
  return '/' + locale + (clean.startsWith('/') ? clean : '/' + clean);
12397
12497
  }
12398
12498
 
12399
- export default { i18nConfig, dictionaries, t, getLocaleFromPath, localePath };
12400
- `;
12499
+ const formatterCache = new Map();
12500
+
12501
+ function getFormatter(kind, locale, options) {
12502
+ const key = kind + ':' + locale + ':' + JSON.stringify(options || {});
12503
+ if (!formatterCache.has(key)) {
12504
+ formatterCache.set(key, new Intl[kind](locale, options));
12505
+ }
12506
+ return formatterCache.get(key);
12507
+ }
12508
+
12509
+ export function getLocaleMeta(locale) {
12510
+ const code = locale || i18nConfig.defaultLocale;
12511
+ return i18nConfig.locales.find(l => l.code === code) || { code, name: code, dir: 'ltr' };
12512
+ }
12513
+
12514
+ export function formatDate(value, options, locale) {
12515
+ return getFormatter('DateTimeFormat', locale || i18nConfig.defaultLocale, options).format(
12516
+ value instanceof Date ? value : new Date(value),
12517
+ );
12518
+ }
12519
+
12520
+ export function formatDateParts(value, options, locale) {
12521
+ return getFormatter('DateTimeFormat', locale || i18nConfig.defaultLocale, options).formatToParts(
12522
+ value instanceof Date ? value : new Date(value),
12523
+ );
12524
+ }
12525
+
12526
+ export function formatNumber(value, options, locale) {
12527
+ return getFormatter('NumberFormat', locale || i18nConfig.defaultLocale, options).format(value);
12528
+ }
12529
+
12530
+ export function formatNumberParts(value, options, locale) {
12531
+ return getFormatter('NumberFormat', locale || i18nConfig.defaultLocale, options).formatToParts(value);
12532
+ }
12533
+
12534
+ export function formatRelativeTime(value, unit, options, locale) {
12535
+ return getFormatter('RelativeTimeFormat', locale || i18nConfig.defaultLocale, options).format(value, unit);
12536
+ }
12537
+
12538
+ export function formatList(values, options, locale) {
12539
+ return getFormatter('ListFormat', locale || i18nConfig.defaultLocale, options).format(values);
12540
+ }
12541
+
12542
+ export function formatListParts(values, options, locale) {
12543
+ return getFormatter('ListFormat', locale || i18nConfig.defaultLocale, options).formatToParts(values);
12544
+ }
12545
+
12546
+ export function formatDisplayName(value, type, options, locale) {
12547
+ if (!Intl.DisplayNames) return String(value);
12548
+ const displayType = type || 'language';
12549
+ return getFormatter('DisplayNames', locale || i18nConfig.defaultLocale, { type: displayType, ...options }).of(value) || String(value);
12550
+ }
12551
+
12552
+ export function createIntl(locale, defaults = {}) {
12553
+ const meta = getLocaleMeta(locale);
12554
+ const code = meta.code;
12555
+ return {
12556
+ locale: code,
12557
+ meta,
12558
+ dir: meta.dir || 'ltr',
12559
+ date: (value, options) => formatDate(value, { ...defaults.date, ...options }, code),
12560
+ dateParts: (value, options) => formatDateParts(value, { ...defaults.date, ...options }, code),
12561
+ number: (value, options) => formatNumber(value, { ...defaults.number, ...options }, code),
12562
+ numberParts: (value, options) => formatNumberParts(value, { ...defaults.number, ...options }, code),
12563
+ relativeTime: (value, unit, options) => formatRelativeTime(value, unit, { ...defaults.relativeTime, ...options }, code),
12564
+ list: (values, options) => formatList(values, { ...defaults.list, ...options }, code),
12565
+ listParts: (values, options) => formatListParts(values, { ...defaults.list, ...options }, code),
12566
+ displayName: (value, type, options) => formatDisplayName(value, type, { ...defaults.displayName, ...options }, code),
12567
+ };
12568
+ }
12569
+
12570
+ export default {
12571
+ i18nConfig,
12572
+ dictionaries,
12573
+ t,
12574
+ getLocaleFromPath,
12575
+ localePath,
12576
+ getLocaleMeta,
12577
+ createIntl,
12578
+ formatDate,
12579
+ formatDateParts,
12580
+ formatNumber,
12581
+ formatNumberParts,
12582
+ formatRelativeTime,
12583
+ formatList,
12584
+ formatListParts,
12585
+ formatDisplayName,
12586
+ };
12587
+ `;
12401
12588
  }
12402
12589
  /**
12403
12590
  * Flattens a nested object into dot-separated keys.
@@ -13223,6 +13410,7 @@ function renderPage(page, options) {
13223
13410
  description: page.description,
13224
13411
  html: page.html,
13225
13412
  toc: page.toc,
13413
+ lastUpdated: page.lastUpdated,
13226
13414
  path: page.path,
13227
13415
  url: page.url,
13228
13416
  frontmatter: page.frontmatter,
@@ -13237,6 +13425,7 @@ function renderPage(page, options) {
13237
13425
  description: p.description,
13238
13426
  html: p.html,
13239
13427
  toc: p.toc,
13428
+ lastUpdated: p.lastUpdated,
13240
13429
  path: p.path,
13241
13430
  url: p.url,
13242
13431
  frontmatter: p.frontmatter,
@@ -13627,18 +13816,52 @@ function resolveCodeAnnotationsOptions(options) {
13627
13816
  */
13628
13817
  function generateVirtualModule(path$2, options) {
13629
13818
  if (path$2 === "config") return `export default ${JSON.stringify(options)};`;
13630
- if (path$2 === "runtime") return `
13819
+ if (path$2 === "runtime") {
13820
+ const base = normalizeRuntimeBase(options.base);
13821
+ return `
13822
+ export const base = ${JSON.stringify(base)};
13823
+ export const runtimeConfig = { base };
13824
+
13825
+ export function isExternalUrl(value) {
13826
+ return /^(?:https?:)?\\/\\//i.test(value) || /^(?:mailto|tel):/i.test(value);
13827
+ }
13828
+
13829
+ export function withBase(pathname = "") {
13830
+ const value = String(pathname);
13831
+ if (!value || value === "/") return base;
13832
+ if (value.startsWith("#") || isExternalUrl(value)) return value;
13833
+ return base + (value.startsWith("/") ? value.slice(1) : value);
13834
+ }
13835
+
13836
+ export function withoutBase(pathname = "") {
13837
+ const value = String(pathname);
13838
+ if (base === "/" || value.startsWith("#") || isExternalUrl(value)) return value;
13839
+ const bareBase = base.slice(0, -1);
13840
+ if (value === bareBase) return "/";
13841
+ if (value.startsWith(base)) return "/" + value.slice(base.length);
13842
+ return value;
13843
+ }
13844
+
13631
13845
  export function useMarkdown() {
13632
13846
  return {
13847
+ base,
13848
+ withBase,
13849
+ withoutBase,
13633
13850
  render: (content) => {
13634
- // Client-side rendering if needed
13635
13851
  return content;
13636
13852
  },
13637
13853
  };
13638
13854
  }
13639
13855
  `;
13856
+ }
13640
13857
  return "export default {};";
13641
13858
  }
13859
+ function normalizeRuntimeBase(base) {
13860
+ const trimmed = base.trim();
13861
+ if (!trimmed || trimmed === "/") return "/";
13862
+ const withLeading = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
13863
+ return withLeading.endsWith("/") ? withLeading : `${withLeading}/`;
13864
+ }
13642
13865
  //#endregion
13643
13866
  exports.DEFAULT_HTML_TEMPLATE = DEFAULT_HTML_TEMPLATE;
13644
13867
  exports.DefaultTheme = DefaultTheme;
@@ -13665,6 +13888,7 @@ exports.generateMarkdown = generateMarkdown;
13665
13888
  exports.generateOgImages = generateOgImages;
13666
13889
  exports.generateTabsCSS = require_tabs.generateTabsCSS;
13667
13890
  exports.generateTypes = generateTypes;
13891
+ exports.generateVirtualModule = generateVirtualModule;
13668
13892
  exports.hasIslands = hasIslands;
13669
13893
  exports.inferType = inferType;
13670
13894
  exports.jsx = jsx;