@raystack/chronicle 0.1.0-canary.a320792 → 0.1.0-canary.ac60f9f

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 (82) hide show
  1. package/dist/cli/index.js +150 -416
  2. package/package.json +13 -9
  3. package/src/cli/commands/build.ts +30 -48
  4. package/src/cli/commands/dev.ts +24 -13
  5. package/src/cli/commands/init.ts +38 -123
  6. package/src/cli/commands/serve.ts +35 -50
  7. package/src/cli/commands/start.ts +20 -16
  8. package/src/cli/index.ts +14 -14
  9. package/src/cli/utils/config.ts +25 -26
  10. package/src/cli/utils/index.ts +3 -2
  11. package/src/cli/utils/resolve.ts +7 -3
  12. package/src/cli/utils/scaffold.ts +14 -16
  13. package/src/components/mdx/code.tsx +1 -10
  14. package/src/components/mdx/details.module.css +24 -1
  15. package/src/components/mdx/details.tsx +3 -2
  16. package/src/components/mdx/image.tsx +5 -20
  17. package/src/components/mdx/index.tsx +3 -3
  18. package/src/components/mdx/link.tsx +24 -20
  19. package/src/components/ui/footer.tsx +2 -3
  20. package/src/components/ui/search.tsx +116 -71
  21. package/src/lib/config.ts +31 -29
  22. package/src/lib/get-llm-text.ts +10 -0
  23. package/src/lib/head.tsx +26 -22
  24. package/src/lib/openapi.ts +8 -8
  25. package/src/lib/page-context.tsx +76 -57
  26. package/src/lib/source.ts +144 -96
  27. package/src/pages/ApiLayout.tsx +22 -18
  28. package/src/pages/ApiPage.tsx +32 -27
  29. package/src/pages/DocsLayout.tsx +7 -7
  30. package/src/pages/DocsPage.tsx +11 -11
  31. package/src/pages/NotFound.tsx +11 -4
  32. package/src/server/App.tsx +35 -27
  33. package/src/server/api/apis-proxy.ts +69 -0
  34. package/src/server/api/health.ts +5 -0
  35. package/src/server/api/page/[...slug].ts +18 -0
  36. package/src/server/api/search.ts +170 -0
  37. package/src/server/api/specs.ts +9 -0
  38. package/src/server/build-search-index.ts +117 -0
  39. package/src/server/entry-client.tsx +52 -56
  40. package/src/server/entry-server.tsx +95 -35
  41. package/src/server/routes/llms.txt.ts +61 -0
  42. package/src/server/routes/og.tsx +75 -0
  43. package/src/server/routes/robots.txt.ts +11 -0
  44. package/src/server/routes/sitemap.xml.ts +39 -0
  45. package/src/server/utils/safe-path.ts +17 -0
  46. package/src/server/vite-config.ts +50 -49
  47. package/src/themes/default/Layout.tsx +69 -41
  48. package/src/themes/default/Page.module.css +0 -60
  49. package/src/themes/default/Page.tsx +9 -11
  50. package/src/themes/default/Toc.tsx +30 -28
  51. package/src/themes/default/index.ts +7 -9
  52. package/src/themes/paper/ChapterNav.tsx +59 -39
  53. package/src/themes/paper/Layout.module.css +1 -1
  54. package/src/themes/paper/Layout.tsx +24 -12
  55. package/src/themes/paper/Page.module.css +11 -4
  56. package/src/themes/paper/Page.tsx +67 -47
  57. package/src/themes/paper/ReadingProgress.tsx +160 -139
  58. package/src/themes/paper/index.ts +5 -5
  59. package/src/themes/registry.ts +7 -7
  60. package/src/types/globals.d.ts +4 -0
  61. package/src/cli/__tests__/config.test.ts +0 -25
  62. package/src/cli/__tests__/scaffold.test.ts +0 -10
  63. package/src/pages/__tests__/head.test.tsx +0 -57
  64. package/src/server/__tests__/entry-server.test.tsx +0 -35
  65. package/src/server/__tests__/handlers.test.ts +0 -77
  66. package/src/server/__tests__/og.test.ts +0 -23
  67. package/src/server/__tests__/router.test.ts +0 -72
  68. package/src/server/__tests__/vite-config.test.ts +0 -25
  69. package/src/server/dev.ts +0 -156
  70. package/src/server/entry-prod.ts +0 -127
  71. package/src/server/handlers/apis-proxy.ts +0 -52
  72. package/src/server/handlers/health.ts +0 -3
  73. package/src/server/handlers/llms.ts +0 -58
  74. package/src/server/handlers/og.ts +0 -87
  75. package/src/server/handlers/robots.ts +0 -11
  76. package/src/server/handlers/search.ts +0 -140
  77. package/src/server/handlers/sitemap.ts +0 -39
  78. package/src/server/handlers/specs.ts +0 -9
  79. package/src/server/index.html +0 -12
  80. package/src/server/prod.ts +0 -18
  81. package/src/server/router.ts +0 -42
  82. package/src/themes/default/font.ts +0 -4
@@ -1,74 +1,70 @@
1
- import { hydrateRoot } from 'react-dom/client'
2
- import { BrowserRouter } from 'react-router-dom'
3
- import { PageProvider } from '@/lib/page-context'
4
- import { App } from './App'
5
- import { getPage, loadPageComponent, buildPageTree } from '@/lib/source'
6
- import { mdxComponents } from '@/components/mdx'
7
- import type { ChronicleConfig, PageTree } from '@/types'
8
- import type { ApiSpec } from '@/lib/openapi'
9
- import type { ReactNode } from 'react'
10
- import React from 'react'
1
+ import '@vitejs/plugin-react/preamble';
2
+ import React from 'react';
3
+ import { hydrateRoot } from 'react-dom/client';
4
+ import { BrowserRouter } from 'react-router';
5
+ import { mdxComponents } from '@/components/mdx';
6
+ import { PageProvider } from '@/lib/page-context';
7
+ import type { ChronicleConfig, Frontmatter, PageTree } from '@/types';
8
+ import type { ApiSpec } from '@/lib/openapi';
9
+ import type { ReactNode } from 'react';
10
+ import { App } from './App';
11
11
 
12
12
  interface EmbeddedData {
13
- config: ChronicleConfig
14
- tree: PageTree
15
- slug: string[]
16
- frontmatter: { title: string; description?: string; order?: number }
17
- filePath: string
13
+ config: ChronicleConfig;
14
+ tree: PageTree;
15
+ slug: string[];
16
+ frontmatter: Frontmatter;
17
+ relativePath: string;
18
18
  }
19
19
 
20
20
  async function hydrate() {
21
21
  try {
22
- const embedded: EmbeddedData | undefined = (window as any).__PAGE_DATA__
22
+ const embedded = (
23
+ window as unknown as { __PAGE_DATA__?: EmbeddedData }
24
+ ).__PAGE_DATA__;
23
25
 
24
- let config: ChronicleConfig = { title: 'Documentation' }
25
- let tree: PageTree = { name: 'root', children: [] }
26
- let page: { slug: string[]; frontmatter: any; content: ReactNode } | null = null
27
- let apiSpecs: ApiSpec[] = []
26
+ const config: ChronicleConfig = embedded?.config ?? {
27
+ title: 'Documentation'
28
+ };
29
+ const tree: PageTree = embedded?.tree ?? { name: 'root', children: [] };
30
+ const isApiPage =
31
+ window.location.pathname.startsWith('/apis') && !!config.api?.length;
32
+ const apiSpecs: ApiSpec[] = isApiPage
33
+ ? await fetch('/api/specs')
34
+ .then(r => r.json())
35
+ .catch(() => [])
36
+ : [];
28
37
 
29
- if (embedded) {
30
- config = embedded.config
31
- tree = embedded.tree
32
-
33
- // Fetch API specs if on /apis route
34
- const isApiRoute = window.location.pathname.startsWith('/apis')
35
- if (isApiRoute && config.api?.length) {
36
- try {
37
- const res = await fetch('/api/specs')
38
- apiSpecs = await res.json()
39
- } catch { /* will load on demand */ }
40
- }
41
-
42
- const sourcePage = await getPage(embedded.slug)
43
- if (sourcePage) {
44
- const component = await loadPageComponent(sourcePage)
45
- page = {
46
- slug: embedded.slug,
47
- frontmatter: embedded.frontmatter,
48
- content: component ? React.createElement(component, { components: mdxComponents }) : null,
49
- }
50
- } else {
51
- page = {
52
- slug: embedded.slug,
53
- frontmatter: embedded.frontmatter,
54
- content: null,
55
- }
56
- }
57
- } else {
58
- tree = await buildPageTree()
59
- }
38
+ const page = embedded?.relativePath
39
+ ? await loadPage(embedded)
40
+ : null;
60
41
 
61
42
  hydrateRoot(
62
43
  document.getElementById('root') as HTMLElement,
63
44
  <BrowserRouter>
64
- <PageProvider initialConfig={config} initialTree={tree} initialPage={page} initialApiSpecs={apiSpecs}>
45
+ <PageProvider
46
+ initialConfig={config}
47
+ initialTree={tree}
48
+ initialPage={page}
49
+ initialApiSpecs={apiSpecs}
50
+ >
65
51
  <App />
66
52
  </PageProvider>
67
- </BrowserRouter>,
68
- )
53
+ </BrowserRouter>
54
+ );
69
55
  } catch (err) {
70
- console.error('Hydration failed:', err)
56
+ console.error('Hydration failed:', err);
71
57
  }
72
58
  }
73
59
 
74
- hydrate()
60
+ async function loadPage(
61
+ embedded: EmbeddedData
62
+ ): Promise<{ slug: string[]; frontmatter: Frontmatter; content: ReactNode }> {
63
+ const mod = await import(/* @vite-ignore */ `/.content/${embedded.relativePath}`);
64
+ const content = mod.default
65
+ ? React.createElement(mod.default, { components: mdxComponents })
66
+ : null;
67
+ return { slug: embedded.slug, frontmatter: embedded.frontmatter, content };
68
+ }
69
+
70
+ hydrate();
@@ -1,35 +1,95 @@
1
- import { renderToString } from 'react-dom/server'
2
- import { StaticRouter } from 'react-router-dom'
3
- import { PageProvider } from '@/lib/page-context'
4
- import { App } from './App'
5
- import type { ReactNode } from 'react'
6
- import type { ChronicleConfig, Frontmatter, PageTree } from '@/types'
7
- import type { ApiSpec } from '@/lib/openapi'
8
-
9
- export interface SSRData {
10
- config: ChronicleConfig
11
- tree: PageTree
12
- page: {
13
- slug: string[]
14
- frontmatter: Frontmatter
15
- content: ReactNode
16
- } | null
17
- apiSpecs: ApiSpec[]
18
- }
19
-
20
- export function render(url: string, data: SSRData): string {
21
- const pathname = new URL(url, 'http://localhost').pathname
22
-
23
- return renderToString(
24
- <StaticRouter location={pathname}>
25
- <PageProvider
26
- initialConfig={data.config}
27
- initialTree={data.tree}
28
- initialPage={data.page}
29
- initialApiSpecs={data.apiSpecs}
30
- >
31
- <App />
32
- </PageProvider>
33
- </StaticRouter>,
34
- )
35
- }
1
+ import '@raystack/apsara/normalize.css';
2
+ import '@raystack/apsara/style.css';
3
+ import path from 'node:path';
4
+ import React from 'react';
5
+ import { renderToReadableStream } from 'react-dom/server.edge';
6
+ import { StaticRouter } from 'react-router';
7
+ import { mdxComponents } from '@/components/mdx';
8
+ import { loadConfig } from '@/lib/config';
9
+ import { loadApiSpecs } from '@/lib/openapi';
10
+ import { PageProvider } from '@/lib/page-context';
11
+ import { buildPageTree, getPage, loadPageComponent } from '@/lib/source';
12
+ import { App } from './App';
13
+
14
+ // @ts-expect-error virtual import from Nitro
15
+ import clientAssets from './entry-client?assets=client';
16
+ // @ts-expect-error virtual import from Nitro
17
+ import serverAssets from './entry-server?assets=ssr';
18
+
19
+ export default {
20
+ async fetch(req: Request) {
21
+ const url = new URL(req.url);
22
+ const pathname = url.pathname;
23
+ const slug = pathname === '/' ? [] : pathname.slice(1).split('/').filter(Boolean);
24
+
25
+ const config = loadConfig();
26
+ const apiSpecs = config.api?.length
27
+ ? await loadApiSpecs(config.api).catch(() => [])
28
+ : [];
29
+
30
+ const [tree, sourcePage] = await Promise.all([
31
+ buildPageTree(),
32
+ getPage(slug),
33
+ ]);
34
+
35
+ const pageData = sourcePage
36
+ ? {
37
+ slug,
38
+ frontmatter: sourcePage.frontmatter,
39
+ content: await loadPageComponent(sourcePage).then(component =>
40
+ component ? React.createElement(component, { components: mdxComponents }) : null
41
+ ),
42
+ }
43
+ : null;
44
+
45
+ const relativePath = sourcePage
46
+ ? path.relative(__CHRONICLE_CONTENT_DIR__, sourcePage.filePath)
47
+ : null;
48
+
49
+ const embeddedData = {
50
+ config,
51
+ tree,
52
+ slug,
53
+ frontmatter: pageData?.frontmatter ?? null,
54
+ relativePath,
55
+ };
56
+ const safeJson = JSON.stringify(embeddedData).replace(/</g, '\\u003c');
57
+
58
+ const assets = clientAssets.merge(serverAssets);
59
+
60
+ const stream = await renderToReadableStream(
61
+ <html lang="en">
62
+ <head>
63
+ <meta charSet="UTF-8" />
64
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
65
+ {assets.css.map((attr: { href: string }) => (
66
+ <link key={attr.href} rel="stylesheet" {...attr} />
67
+ ))}
68
+ {assets.js.map((attr: { href: string }) => (
69
+ <link key={attr.href} rel="modulepreload" {...attr} />
70
+ ))}
71
+ <script type="module" src={assets.entry} />
72
+ <script dangerouslySetInnerHTML={{ __html: `window.__PAGE_DATA__ = ${safeJson}` }} />
73
+ </head>
74
+ <body>
75
+ <div id="root">
76
+ <StaticRouter location={pathname}>
77
+ <PageProvider
78
+ initialConfig={config}
79
+ initialTree={tree}
80
+ initialPage={pageData}
81
+ initialApiSpecs={apiSpecs}
82
+ >
83
+ <App />
84
+ </PageProvider>
85
+ </StaticRouter>
86
+ </div>
87
+ </body>
88
+ </html>,
89
+ );
90
+
91
+ return new Response(stream, {
92
+ headers: { 'Content-Type': 'text/html;charset=utf-8' },
93
+ });
94
+ },
95
+ };
@@ -0,0 +1,61 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import matter from 'gray-matter';
4
+ import { defineHandler, HTTPError } from 'nitro';
5
+ import { loadConfig } from '@/lib/config';
6
+
7
+ function getContentDir(): string {
8
+ return __CHRONICLE_CONTENT_DIR__ || path.join(process.cwd(), 'content');
9
+ }
10
+
11
+ async function scanPages(): Promise<{ title: string; url: string }[]> {
12
+ const contentDir = getContentDir();
13
+ const pages: { title: string; url: string }[] = [];
14
+
15
+ async function scan(dir: string, prefix: string[] = []) {
16
+ try {
17
+ const entries = await fs.readdir(dir, { withFileTypes: true });
18
+ for (const entry of entries) {
19
+ if (entry.name.startsWith('.') || entry.name === 'node_modules')
20
+ continue;
21
+ const fullPath = path.join(dir, entry.name);
22
+
23
+ if (entry.isDirectory()) {
24
+ await scan(fullPath, [...prefix, entry.name]);
25
+ continue;
26
+ }
27
+
28
+ if (!entry.name.endsWith('.mdx') && !entry.name.endsWith('.md'))
29
+ continue;
30
+
31
+ const raw = await fs.readFile(fullPath, 'utf-8');
32
+ const { data: fm } = matter(raw);
33
+ const baseName = entry.name.replace(/\.(mdx|md)$/, '');
34
+ const slugs = baseName === 'index' ? prefix : [...prefix, baseName];
35
+ const url = slugs.length === 0 ? '/' : `/${slugs.join('/')}`;
36
+
37
+ pages.push({ title: fm.title ?? baseName, url });
38
+ }
39
+ } catch {
40
+ /* directory not readable */
41
+ }
42
+ }
43
+
44
+ await scan(contentDir);
45
+ return pages;
46
+ }
47
+
48
+ export default defineHandler(async event => {
49
+ const config = loadConfig();
50
+
51
+ if (!config.llms?.enabled) {
52
+ throw new HTTPError({ status: 404, message: 'Not Found' });
53
+ }
54
+
55
+ const pages = await scanPages();
56
+ const index = pages.map(p => `- [${p.title}](${p.url})`).join('\n');
57
+ const body = `# ${config.title}\n\n${config.description ?? ''}\n\n${index}`;
58
+
59
+ event.res.headers.set('Content-Type', 'text/plain');
60
+ return body;
61
+ });
@@ -0,0 +1,75 @@
1
+ import { defineHandler } from 'nitro';
2
+ import React from 'react';
3
+ import satori from 'satori';
4
+ import { loadConfig } from '@/lib/config';
5
+
6
+ let fontData: ArrayBuffer | null = null;
7
+
8
+ async function loadFont(): Promise<ArrayBuffer> {
9
+ if (fontData) return fontData;
10
+
11
+ try {
12
+ const response = await fetch(
13
+ 'https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hiA.woff2'
14
+ );
15
+ fontData = await response.arrayBuffer();
16
+ } catch {
17
+ fontData = new ArrayBuffer(0);
18
+ }
19
+
20
+ return fontData;
21
+ }
22
+
23
+ export default defineHandler(async event => {
24
+ const config = loadConfig();
25
+ const title = event.url.searchParams.get('title') ?? config.title;
26
+ const description = event.url.searchParams.get('description') ?? '';
27
+ const siteName = config.title;
28
+
29
+ const font = await loadFont();
30
+
31
+ const svg = await satori(
32
+ <div
33
+ style={{
34
+ height: '100%',
35
+ width: '100%',
36
+ display: 'flex',
37
+ flexDirection: 'column',
38
+ justifyContent: 'center',
39
+ padding: '60px 80px',
40
+ backgroundColor: '#0a0a0a',
41
+ color: '#fafafa',
42
+ }}
43
+ >
44
+ <div style={{ fontSize: 24, color: '#888', marginBottom: 16 }}>
45
+ {siteName}
46
+ </div>
47
+ <div
48
+ style={{
49
+ fontSize: 56,
50
+ fontWeight: 700,
51
+ lineHeight: 1.2,
52
+ marginBottom: 24,
53
+ }}
54
+ >
55
+ {title}
56
+ </div>
57
+ {description && (
58
+ <div style={{ fontSize: 24, color: '#999', lineHeight: 1.4 }}>
59
+ {description}
60
+ </div>
61
+ )}
62
+ </div>,
63
+ {
64
+ width: 1200,
65
+ height: 630,
66
+ fonts: [
67
+ { name: 'Inter', data: font, weight: 400, style: 'normal' as const },
68
+ ],
69
+ },
70
+ );
71
+
72
+ event.res.headers.set('Content-Type', 'image/svg+xml');
73
+ event.res.headers.set('Cache-Control', 'public, max-age=86400');
74
+ return svg;
75
+ });
@@ -0,0 +1,11 @@
1
+ import { defineHandler } from 'nitro';
2
+ import { loadConfig } from '@/lib/config';
3
+
4
+ export default defineHandler(event => {
5
+ const config = loadConfig();
6
+ const sitemap = config.url ? `\nSitemap: ${config.url}/sitemap.xml` : '';
7
+ const body = `User-agent: *\nAllow: /${sitemap}`;
8
+
9
+ event.res.headers.set('Content-Type', 'text/plain');
10
+ return body;
11
+ });
@@ -0,0 +1,39 @@
1
+ import { defineHandler } from 'nitro';
2
+ import { buildApiRoutes } from '@/lib/api-routes';
3
+ import { loadConfig } from '@/lib/config';
4
+ import { loadApiSpecs } from '@/lib/openapi';
5
+ import { getPages } from '@/lib/source';
6
+
7
+ export default defineHandler(async event => {
8
+ const config = loadConfig();
9
+
10
+ if (!config.url) {
11
+ event.res.headers.set('Content-Type', 'application/xml');
12
+ return '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"/>';
13
+ }
14
+
15
+ const baseUrl = config.url.replace(/\/$/, '');
16
+
17
+ const pages = await getPages();
18
+ const docPages = pages.map(page => {
19
+ const lastmod = page.frontmatter.lastModified
20
+ ? `<lastmod>${new Date(page.frontmatter.lastModified).toISOString()}</lastmod>`
21
+ : '';
22
+ return `<url><loc>${baseUrl}/${page.slugs.join('/')}</loc>${lastmod}</url>`;
23
+ });
24
+
25
+ const apiPages = config.api?.length
26
+ ? buildApiRoutes(await loadApiSpecs(config.api)).map(
27
+ route => `<url><loc>${baseUrl}/apis/${route.slug.join('/')}</loc></url>`
28
+ )
29
+ : [];
30
+
31
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
32
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
33
+ <url><loc>${baseUrl}</loc></url>
34
+ ${[...docPages, ...apiPages].join('\n')}
35
+ </urlset>`;
36
+
37
+ event.res.headers.set('Content-Type', 'application/xml');
38
+ return xml;
39
+ });
@@ -0,0 +1,17 @@
1
+ import path from 'node:path';
2
+
3
+ /**
4
+ * Resolve a URL path within a base directory, preventing path traversal.
5
+ * Returns null if the resolved path escapes the base directory.
6
+ */
7
+ export function safePath(baseDir: string, urlPath: string): string | null {
8
+ const decoded = decodeURIComponent(urlPath.split('?')[0]);
9
+ const resolved = path.resolve(baseDir, '.' + decoded);
10
+ if (
11
+ !resolved.startsWith(path.resolve(baseDir) + path.sep) &&
12
+ resolved !== path.resolve(baseDir)
13
+ ) {
14
+ return null;
15
+ }
16
+ return resolved;
17
+ }
@@ -1,71 +1,72 @@
1
- import path from 'path'
2
- import { type InlineConfig } from 'vite'
3
- import react from '@vitejs/plugin-react'
4
- import mdx from '@mdx-js/rollup'
5
- import remarkDirective from 'remark-directive'
6
- import remarkGfm from 'remark-gfm'
7
- import remarkFrontmatter from 'remark-frontmatter'
8
- import remarkMdxFrontmatter from 'remark-mdx-frontmatter'
9
- import rehypeShiki from '@shikijs/rehype'
1
+ import react from '@vitejs/plugin-react';
2
+ import mdx from 'fumadocs-mdx/vite';
3
+ import { nitro } from 'nitro/vite';
4
+ import path from 'node:path';
5
+ import { type InlineConfig } from 'vite';
10
6
 
11
7
  export interface ViteConfigOptions {
12
- root: string
13
- contentDir: string
14
- isDev?: boolean
8
+ packageRoot: string;
9
+ projectRoot: string;
10
+ contentDir: string;
11
+ preset?: string;
15
12
  }
16
13
 
17
- export async function createViteConfig(options: ViteConfigOptions): Promise<InlineConfig> {
18
- const { root, contentDir, isDev = false } = options
14
+ export async function createViteConfig(
15
+ options: ViteConfigOptions
16
+ ): Promise<InlineConfig> {
17
+ const { packageRoot, projectRoot, contentDir, preset } = options;
19
18
 
20
19
  return {
21
- root,
20
+ root: packageRoot,
22
21
  configFile: false,
22
+ plugins: [
23
+ nitro({
24
+ serverDir: path.resolve(packageRoot, 'src/server'),
25
+ ...(preset && { preset }),
26
+ }),
27
+ mdx({}, { index: false }),
28
+ react()
29
+ ],
23
30
  resolve: {
24
31
  alias: {
25
- '@': path.resolve(root, 'src'),
26
- '@content': contentDir,
32
+ '@': path.resolve(packageRoot, 'src'),
33
+ '@content': path.resolve(packageRoot, '.content'),
27
34
  },
28
- dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime'],
35
+ conditions: ['module-sync', 'import', 'node'],
36
+ dedupe: [
37
+ 'react',
38
+ 'react-dom',
39
+ 'react/jsx-runtime',
40
+ 'react/jsx-dev-runtime',
41
+ 'react-router',
42
+ ]
29
43
  },
30
44
  server: {
31
45
  fs: {
32
- allow: [root, contentDir],
33
- },
46
+ allow: [packageRoot, projectRoot, contentDir]
47
+ }
34
48
  },
35
- plugins: [
36
- mdx({
37
- remarkPlugins: [
38
- remarkFrontmatter,
39
- remarkMdxFrontmatter,
40
- remarkGfm,
41
- remarkDirective,
42
- ],
43
- rehypePlugins: [
44
- [rehypeShiki, { themes: { light: 'github-light', dark: 'github-dark' }, defaultColor: false }],
45
- ],
46
- mdExtensions: ['.md'],
47
- mdxExtensions: ['.mdx'],
48
- }),
49
- react(),
50
- ],
51
49
  define: {
52
- 'process.env.CHRONICLE_CONTENT_DIR': JSON.stringify(contentDir),
53
- 'process.env.CHRONICLE_PROJECT_ROOT': JSON.stringify(root),
50
+ __CHRONICLE_CONTENT_DIR__: JSON.stringify(contentDir),
51
+ __CHRONICLE_PROJECT_ROOT__: JSON.stringify(projectRoot),
52
+ __CHRONICLE_PACKAGE_ROOT__: JSON.stringify(packageRoot)
54
53
  },
55
54
  css: {
56
55
  modules: {
57
- localsConvention: 'camelCase',
58
- },
56
+ localsConvention: 'camelCase'
57
+ }
59
58
  },
60
59
  ssr: {
61
- noExternal: ['@raystack/apsara'],
62
- },
63
- build: {
64
- rolldownOptions: {
65
- input: isDev ? undefined : {
66
- client: path.resolve(root, 'src/server/index.html'),
67
- },
68
- },
60
+ noExternal: ['@raystack/apsara', 'dayjs', 'fumadocs-core']
69
61
  },
70
- }
62
+ environments: {
63
+ client: {
64
+ build: {
65
+ rollupOptions: {
66
+ input: path.resolve(packageRoot, 'src/server/entry-client.tsx')
67
+ }
68
+ }
69
+ }
70
+ }
71
+ };
71
72
  }