orga-build 0.5.4 → 0.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.
package/README.org CHANGED
@@ -78,6 +78,10 @@ export default {
78
78
  // CSS class(es) to wrap rendered org content
79
79
  containerClass: ['prose', 'prose-lg'],
80
80
 
81
+ // Global stylesheet URLs (explicit, no magic files)
82
+ // These are injected in dev SSR <head> and imported by the client entry.
83
+ styles: ['/style.css'],
84
+
81
85
  // Additional Vite plugins
82
86
  vitePlugins: []
83
87
  }
@@ -90,6 +94,7 @@ export default {
90
94
  | =root= | =string= | ='pages'= | Directory containing content files |
91
95
  | =outDir= | =string= | ='out'= | Output directory for production build |
92
96
  | =containerClass= | =string \vert string[]= | =[]= | CSS class(es) for content wrapper |
97
+ | =styles= | =string[]= | =[]= | Global stylesheet URLs injected/imported explicitly |
93
98
  | =vitePlugins= | =PluginOption[]= | =[]= | Additional Vite plugins |
94
99
 
95
100
  * TypeScript Setup
package/cli.js CHANGED
@@ -16,6 +16,11 @@ const { positionals } = parseArgs({
16
16
  allowPositionals: true
17
17
  })
18
18
 
19
- const config = await loadConfig('orga.config.js', 'orga.config.mjs')
19
+ const { config, projectRoot } = await loadConfig(
20
+ 'orga.config.js',
21
+ 'orga.config.mjs'
22
+ )
20
23
 
21
- await (positionals.includes('dev') ? serve(config) : build(config))
24
+ await (positionals.includes('dev')
25
+ ? serve(config, 3000, projectRoot)
26
+ : build(config, projectRoot))
package/lib/build.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * @param {import('./config.js').Config} config
3
+ * @param {string} [projectRoot]
3
4
  */
4
- export function build({ outDir, root, containerClass, vitePlugins }: import("./config.js").Config): Promise<void>;
5
+ export function build({ outDir, root, containerClass, styles, vitePlugins }: import("./config.js").Config, projectRoot?: string): Promise<void>;
5
6
  export { alias };
6
7
  import { alias } from './plugin.js';
7
8
  //# sourceMappingURL=build.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["build.js"],"names":[],"mappings":"AAcA;;GAEG;AACH,qEAFW,OAAO,aAAa,EAAE,MAAM,iBAmJtC;;sBA7J4C,aAAa"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["build.js"],"names":[],"mappings":"AAeA;;;GAGG;AACH,6EAHW,OAAO,aAAa,EAAE,MAAM,gBAC5B,MAAM,iBAoJhB;;sBAhK4C,aAAa"}
package/lib/build.js CHANGED
@@ -4,6 +4,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
4
4
  import { emptyDir, ensureDir, exists } from './fs.js'
5
5
  import fs from 'fs/promises'
6
6
  import { createOrgaBuildConfig, alias } from './plugin.js'
7
+ import { escapeHtml } from './util.js'
7
8
 
8
9
  // Re-export alias for backwards compatibility
9
10
  export { alias }
@@ -14,13 +15,15 @@ const defaultIndexHtml = fileURLToPath(new URL('./index.html', import.meta.url))
14
15
 
15
16
  /**
16
17
  * @param {import('./config.js').Config} config
18
+ * @param {string} [projectRoot]
17
19
  */
18
20
  export async function build({
19
21
  outDir,
20
22
  root,
21
23
  containerClass,
24
+ styles = [],
22
25
  vitePlugins = []
23
- }) {
26
+ }, projectRoot = process.cwd()) {
24
27
  await emptyDir(outDir)
25
28
  const ssrOutDir = path.join(outDir, '.ssr')
26
29
  const clientOutDir = outDir
@@ -29,6 +32,7 @@ export async function build({
29
32
  root,
30
33
  outDir,
31
34
  containerClass,
35
+ styles,
32
36
  vitePlugins
33
37
  })
34
38
 
@@ -100,7 +104,6 @@ export async function build({
100
104
 
101
105
  /* --- get html template, inject entry js and css --- */
102
106
  // Check for user's index.html in project root, otherwise use default
103
- const projectRoot = process.cwd()
104
107
  const userIndexPath = path.join(projectRoot, 'index.html')
105
108
  const indexHtmlPath = (await exists(userIndexPath)) ? userIndexPath : defaultIndexHtml
106
109
  const template = await fs.readFile(indexHtmlPath, { encoding: 'utf-8' })
@@ -150,12 +153,12 @@ export async function build({
150
153
 
151
154
  html = html.replace('</head>', `${css}</head>`)
152
155
 
153
- const page = pages[pagePath]
154
- if (page && page.title) {
155
- html = html.replace(
156
- /<title>(.*?)<\/title>/,
157
- `<title>${page.title}</title>`
158
- )
156
+ const page = pages[pagePath]
157
+ if (page) {
158
+ html = html.replace(/%orga\.(\w+)%/g, (_, key) => {
159
+ const value = page[key] ?? ''
160
+ return escapeHtml(String(value))
161
+ })
159
162
  }
160
163
 
161
164
  return html
package/lib/config.d.ts CHANGED
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * @param {string[]} files
3
- * @returns {Promise<Config>}
3
+ * @returns {Promise<{ config: Config, projectRoot: string }>}
4
4
  */
5
- export function loadConfig(...files: string[]): Promise<Config>;
5
+ export function loadConfig(...files: string[]): Promise<{
6
+ config: Config;
7
+ projectRoot: string;
8
+ }>;
6
9
  export type Config = {
7
10
  outDir: string;
8
11
  root: string;
@@ -13,5 +16,9 @@ export type Config = {
13
16
  */
14
17
  vitePlugins: import("vite").PluginOption[];
15
18
  containerClass: string[] | string;
19
+ /**
20
+ * - Global stylesheet URLs injected in dev SSR and imported by client entry
21
+ */
22
+ styles?: string[];
16
23
  };
17
24
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["config.js"],"names":[],"mappings":"AAuBA;;;GAGG;AACH,qCAHW,MAAM,EAAE,GACN,OAAO,CAAC,MAAM,CAAC,CAsC3B;;YA1Da,MAAM;UACN,MAAM;cACN,MAAM,EAAE;eACR,MAAM,EAAE;;;;iBACR,OAAO,MAAM,EAAE,YAAY,EAAE;oBAC7B,MAAM,EAAE,GAAC,MAAM"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["config.js"],"names":[],"mappings":"AAyBA;;;GAGG;AACH,qCAHW,MAAM,EAAE,GACN,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CA6C5D;;YAnEa,MAAM;UACN,MAAM;cACN,MAAM,EAAE;eACR,MAAM,EAAE;;;;iBACR,OAAO,MAAM,EAAE,YAAY,EAAE;oBAC7B,MAAM,EAAE,GAAC,MAAM;;;;aACf,MAAM,EAAE"}
package/lib/config.js CHANGED
@@ -9,6 +9,7 @@ import path from 'node:path'
9
9
  * @property {string[]} postBuild
10
10
  * @property {import('vite').PluginOption[]} vitePlugins - Array of Vite plugins
11
11
  * @property {string[]|string} containerClass
12
+ * @property {string[]} [styles] - Global stylesheet URLs injected in dev SSR and imported by client entry
12
13
  */
13
14
 
14
15
  /** @type {Config} */
@@ -18,12 +19,13 @@ const defaultConfig = {
18
19
  preBuild: [],
19
20
  postBuild: [],
20
21
  vitePlugins: [],
21
- containerClass: []
22
+ containerClass: [],
23
+ styles: []
22
24
  }
23
25
 
24
26
  /**
25
27
  * @param {string[]} files
26
- * @returns {Promise<Config>}
28
+ * @returns {Promise<{ config: Config, projectRoot: string }>}
27
29
  */
28
30
  export async function loadConfig(...files) {
29
31
  const cwd = process.cwd()
@@ -35,6 +37,7 @@ export async function loadConfig(...files) {
35
37
  path.isAbsolute(value) ? value : path.resolve(cwd, value)
36
38
 
37
39
  let result = { ...defaultConfig }
40
+ let configPath = null
38
41
 
39
42
  for (const file of files) {
40
43
  const filePath = path.join(cwd, file)
@@ -51,6 +54,7 @@ export async function loadConfig(...files) {
51
54
  // Support both default export (recommended) and named exports
52
55
  const config = module.default || module
53
56
  result = { ...defaultConfig, ...config }
57
+ configPath = filePath
54
58
  break
55
59
  } catch (err) {
56
60
  // Config file exists but has errors
@@ -60,5 +64,10 @@ export async function loadConfig(...files) {
60
64
 
61
65
  result.root = resolveConfigPath(result.root)
62
66
  result.outDir = resolveConfigPath(result.outDir)
63
- return result
67
+ const styles = result.styles
68
+ result.styles = Array.isArray(styles) ? styles.filter((v) => typeof v === 'string') : []
69
+ return {
70
+ config: result,
71
+ projectRoot: configPath ? path.dirname(configPath) : cwd
72
+ }
64
73
  }
package/lib/index.html CHANGED
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8"/>
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
- <title>📘Orga Pages</title>
6
+ <title>%orga.title%</title>
7
7
  </head>
8
8
  <body>
9
9
  <div id="root"></div>
package/lib/plugin.d.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  * @property {string} root - Root directory for content files
4
4
  * @property {string | undefined} [outDir] - Output directory (excluded from file discovery)
5
5
  * @property {string|string[]} [containerClass] - CSS class(es) to wrap rendered content
6
+ * @property {string[]} [styles] - Global stylesheet URLs to import/inject
6
7
  */
7
8
  /**
8
9
  * Creates the canonical orga-build plugin preset.
@@ -11,7 +12,7 @@
11
12
  * @param {OrgaBuildPluginOptions} options
12
13
  * @returns {import('vite').PluginOption[]}
13
14
  */
14
- export function orgaBuildPlugin({ root, outDir, containerClass }: OrgaBuildPluginOptions): import("vite").PluginOption[];
15
+ export function orgaBuildPlugin({ root, outDir, containerClass, styles }: OrgaBuildPluginOptions): import("vite").PluginOption[];
15
16
  /**
16
17
  * Creates the full Vite config options for orga-build.
17
18
  * Includes plugins, resolve aliases, and other shared config.
@@ -19,7 +20,7 @@ export function orgaBuildPlugin({ root, outDir, containerClass }: OrgaBuildPlugi
19
20
  * @param {OrgaBuildPluginOptions & { outDir?: string, vitePlugins?: import('vite').PluginOption[], includeFallbackHtml?: boolean, projectRoot?: string }} options
20
21
  * @returns {{ plugins: import('vite').PluginOption[], resolve: { alias: typeof alias } }}
21
22
  */
22
- export function createOrgaBuildConfig({ root, outDir, containerClass, vitePlugins, includeFallbackHtml, projectRoot }: OrgaBuildPluginOptions & {
23
+ export function createOrgaBuildConfig({ root, outDir, containerClass, styles, vitePlugins, includeFallbackHtml, projectRoot }: OrgaBuildPluginOptions & {
23
24
  outDir?: string;
24
25
  vitePlugins?: import("vite").PluginOption[];
25
26
  includeFallbackHtml?: boolean;
@@ -33,17 +34,18 @@ export function createOrgaBuildConfig({ root, outDir, containerClass, vitePlugin
33
34
  /**
34
35
  * Creates an HTML serving plugin that handles index.html for dev mode.
35
36
  *
36
- * This plugin:
37
- * - Serves user's index.html from project root if present, otherwise uses the default template
37
+ * This plugin performs per-request SSR in dev mode (matching Astro/SvelteKit behaviour):
38
+ * - SSR-renders each page on every request using Vite's server module runner
39
+ * - Injects rendered content and page metadata (%orga.*% placeholders) into the template
40
+ * - Falls back to the shell HTML for unknown routes (client-side router handles 404)
38
41
  * - Only handles GET/HEAD requests that accept HTML
39
- * - Runs late (post middleware) so other plugins get first chance
40
- * - Passes HTML through transformIndexHtml for ecosystem compatibility
41
42
  * - Does not intercept asset requests
42
43
  *
43
44
  * @param {string} projectRoot - Project root directory (where orga.config.js lives)
45
+ * @param {string[]} [styles]
44
46
  * @returns {import('vite').Plugin}
45
47
  */
46
- export function htmlFallbackPlugin(projectRoot: string): import("vite").Plugin;
48
+ export function htmlFallbackPlugin(projectRoot: string, styles?: string[]): import("vite").Plugin;
47
49
  /**
48
50
  * Alias map for React and wouter to ensure consistent resolution
49
51
  */
@@ -65,5 +67,9 @@ export type OrgaBuildPluginOptions = {
65
67
  * - CSS class(es) to wrap rendered content
66
68
  */
67
69
  containerClass?: string | string[];
70
+ /**
71
+ * - Global stylesheet URLs to import/inject
72
+ */
73
+ styles?: string[];
68
74
  };
69
75
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["plugin.js"],"names":[],"mappings":"AAoBA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,kEAHW,sBAAsB,GACpB,OAAO,MAAM,EAAE,YAAY,EAAE,CAQzC;AAED;;;;;;GAMG;AACH,uHAHW,sBAAsB,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,MAAM,EAAE,YAAY,EAAE,CAAC;IAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5I;IAAE,OAAO,EAAE,OAAO,MAAM,EAAE,YAAY,EAAE,CAAC;IAAC,OAAO,EAAE;QAAE,KAAK,EAAE,OAAO,KAAK,CAAA;KAAE,CAAA;CAAE,CAoBxF;AAiBD;;;;;;;;;;;;GAYG;AACH,gDAHW,MAAM,GACJ,OAAO,MAAM,EAAE,MAAM,CA8CjC;AAlID;;GAEG;AACH;;;;EAIC;;;;;UAIa,MAAM;;;;aACN,MAAM,GAAG,SAAS;;;;qBAClB,MAAM,GAAC,MAAM,EAAE"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["plugin.js"],"names":[],"mappings":"AAuBA;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,0EAHW,sBAAsB,GACpB,OAAO,MAAM,EAAE,YAAY,EAAE,CAazC;AAED;;;;;;GAMG;AACH,+HAHW,sBAAsB,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,MAAM,EAAE,YAAY,EAAE,CAAC;IAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5I;IAAE,OAAO,EAAE,OAAO,MAAM,EAAE,YAAY,EAAE,CAAC;IAAC,OAAO,EAAE;QAAE,KAAK,EAAE,OAAO,KAAK,CAAA;KAAE,CAAA;CAAE,CAwBxF;AAiBD;;;;;;;;;;;;;GAaG;AACH,gDAJW,MAAM,WACN,MAAM,EAAE,GACN,OAAO,MAAM,EAAE,MAAM,CAmFjC;AAlLD;;GAEG;AACH;;;;EAIC;;;;;UAIa,MAAM;;;;aACN,MAAM,GAAG,SAAS;;;;qBAClB,MAAM,GAAC,MAAM,EAAE;;;;aACf,MAAM,EAAE"}
package/lib/plugin.js CHANGED
@@ -2,13 +2,16 @@ import path from 'node:path'
2
2
  import fs from 'node:fs/promises'
3
3
  import { createRequire } from 'node:module'
4
4
  import { fileURLToPath } from 'node:url'
5
+ import { createServerModuleRunner } from 'vite'
5
6
  import react from '@vitejs/plugin-react'
6
7
  import { setupOrga } from './orga.js'
7
8
  import { pluginFactory } from './vite.js'
9
+ import { escapeHtml } from './util.js'
10
+
11
+ const ssrEntry = fileURLToPath(new URL('./ssr.jsx', import.meta.url))
8
12
 
9
13
  const require = createRequire(import.meta.url)
10
14
  const defaultIndexHtml = fileURLToPath(new URL('./index.html', import.meta.url))
11
-
12
15
  /**
13
16
  * Alias map for React and wouter to ensure consistent resolution
14
17
  */
@@ -23,6 +26,7 @@ export const alias = {
23
26
  * @property {string} root - Root directory for content files
24
27
  * @property {string | undefined} [outDir] - Output directory (excluded from file discovery)
25
28
  * @property {string|string[]} [containerClass] - CSS class(es) to wrap rendered content
29
+ * @property {string[]} [styles] - Global stylesheet URLs to import/inject
26
30
  */
27
31
 
28
32
  /**
@@ -32,11 +36,16 @@ export const alias = {
32
36
  * @param {OrgaBuildPluginOptions} options
33
37
  * @returns {import('vite').PluginOption[]}
34
38
  */
35
- export function orgaBuildPlugin({ root, outDir, containerClass = [] }) {
39
+ export function orgaBuildPlugin({
40
+ root,
41
+ outDir,
42
+ containerClass = [],
43
+ styles = []
44
+ }) {
36
45
  return [
37
46
  setupOrga({ containerClass, root }),
38
47
  react(),
39
- pluginFactory({ dir: root, outDir })
48
+ pluginFactory({ dir: root, outDir, styles })
40
49
  ]
41
50
  }
42
51
 
@@ -51,15 +60,19 @@ export function createOrgaBuildConfig({
51
60
  root,
52
61
  outDir,
53
62
  containerClass = [],
63
+ styles = [],
54
64
  vitePlugins = [],
55
65
  includeFallbackHtml = false,
56
66
  projectRoot = process.cwd()
57
67
  }) {
58
- const plugins = [...vitePlugins, ...orgaBuildPlugin({ root, outDir, containerClass })]
68
+ const plugins = [
69
+ ...vitePlugins,
70
+ ...orgaBuildPlugin({ root, outDir, containerClass, styles })
71
+ ]
59
72
  if (includeFallbackHtml) {
60
73
  // HTML fallback must be first so it can handle HTML navigation requests
61
74
  // before runtime plugins (e.g. Cloudflare) potentially return 404.
62
- plugins.unshift(htmlFallbackPlugin(projectRoot))
75
+ plugins.unshift(htmlFallbackPlugin(projectRoot, styles))
63
76
  }
64
77
  return {
65
78
  plugins,
@@ -85,17 +98,18 @@ async function hasUserIndexHtml(root) {
85
98
  /**
86
99
  * Creates an HTML serving plugin that handles index.html for dev mode.
87
100
  *
88
- * This plugin:
89
- * - Serves user's index.html from project root if present, otherwise uses the default template
101
+ * This plugin performs per-request SSR in dev mode (matching Astro/SvelteKit behaviour):
102
+ * - SSR-renders each page on every request using Vite's server module runner
103
+ * - Injects rendered content and page metadata (%orga.*% placeholders) into the template
104
+ * - Falls back to the shell HTML for unknown routes (client-side router handles 404)
90
105
  * - Only handles GET/HEAD requests that accept HTML
91
- * - Runs late (post middleware) so other plugins get first chance
92
- * - Passes HTML through transformIndexHtml for ecosystem compatibility
93
106
  * - Does not intercept asset requests
94
107
  *
95
108
  * @param {string} projectRoot - Project root directory (where orga.config.js lives)
109
+ * @param {string[]} [styles]
96
110
  * @returns {import('vite').Plugin}
97
111
  */
98
- export function htmlFallbackPlugin(projectRoot) {
112
+ export function htmlFallbackPlugin(projectRoot, styles = []) {
99
113
  return {
100
114
  name: 'orga-build:html-fallback',
101
115
 
@@ -106,7 +120,11 @@ export function htmlFallbackPlugin(projectRoot) {
106
120
  const userHasIndex = await hasUserIndexHtml(projectRoot)
107
121
  const indexHtmlPath = userHasIndex ? userIndexPath : defaultIndexHtml
108
122
 
109
- // Add middleware to serve HTML early in the chain.
123
+ // CJS compatibility here depends on Vite externalizing bare-specifier deps
124
+ // (e.g. react/*) to native Node import(). If aliases rewrite specifiers
125
+ // to absolute paths, modules can be inlined and evaluated without CJS globals.
126
+ const runner = createServerModuleRunner(server.environments.ssr)
127
+
110
128
  server.middlewares.use(async (req, res, next) => {
111
129
  // Only handle GET/HEAD requests
112
130
  if (req.method !== 'GET' && req.method !== 'HEAD') {
@@ -128,8 +146,41 @@ export function htmlFallbackPlugin(projectRoot) {
128
146
  }
129
147
 
130
148
  try {
149
+ // Import via the runner on each request — the module graph handles
150
+ // HMR invalidation so stale modules are never served.
151
+ const { render, pages } = await runner.import(ssrEntry)
152
+ const content = render(pathname)
153
+
131
154
  let html = await fs.readFile(indexHtmlPath, 'utf-8')
132
155
  html = await server.transformIndexHtml(url, html)
156
+
157
+ const uniqueCssUrls = [...new Set(styles)]
158
+ if (uniqueCssUrls.length > 0) {
159
+ const cssLinks = uniqueCssUrls
160
+ .map((u) => `<link rel="stylesheet" href="${escapeHtml(u)}">`)
161
+ .join('')
162
+ html = html.replace('</head>', `${cssLinks}</head>`)
163
+ }
164
+
165
+ if (content) {
166
+ const ssr = { routePath: pathname }
167
+ html = html.replace(
168
+ '<div id="root"></div>',
169
+ `<script>window._ssr=${JSON.stringify(ssr)};</script><div id="root">${content}</div>`
170
+ )
171
+ }
172
+
173
+ // Replace %orga.*% placeholders with page metadata
174
+ const page = pages[pathname]
175
+ if (page) {
176
+ html = html.replace(/%orga\.(\w+)%/g, (_, key) => {
177
+ const value = page[key] ?? ''
178
+ return escapeHtml(String(value))
179
+ })
180
+ }
181
+ // Strip any remaining unresolved placeholders (unknown route)
182
+ html = html.replace(/%orga\.\w+%/g, '')
183
+
133
184
  res.statusCode = 200
134
185
  res.setHeader('Content-Type', 'text/html')
135
186
  res.end(html)
package/lib/serve.d.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * @param {import('./config.js').Config} config
5
5
  * @param {number} [port]
6
+ * @param {string} [projectRoot]
6
7
  */
7
- export function serve(config: import("./config.js").Config, port?: number): Promise<void>;
8
+ export function serve(config: import("./config.js").Config, port?: number, projectRoot?: string): Promise<void>;
8
9
  //# sourceMappingURL=serve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["serve.js"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,8BAHW,OAAO,aAAa,EAAE,MAAM,SAC5B,MAAM,iBA2BhB"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["serve.js"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,8BAJW,OAAO,aAAa,EAAE,MAAM,SAC5B,MAAM,gBACN,MAAM,iBAsChB"}
package/lib/serve.js CHANGED
@@ -1,27 +1,39 @@
1
1
  import path from 'node:path'
2
2
  import { createServer } from 'vite'
3
- import { createOrgaBuildConfig } from './plugin.js'
3
+ import { createOrgaBuildConfig, alias } from './plugin.js'
4
4
 
5
5
  /**
6
6
  * Start the development server using native Vite.
7
7
  *
8
8
  * @param {import('./config.js').Config} config
9
9
  * @param {number} [port]
10
+ * @param {string} [projectRoot]
10
11
  */
11
- export async function serve(config, port = 3000) {
12
- const { plugins, resolve } = createOrgaBuildConfig({
12
+ export async function serve(config, port = 3000, projectRoot = process.cwd()) {
13
+ const { plugins } = createOrgaBuildConfig({
13
14
  root: config.root,
14
15
  outDir: config.outDir,
15
16
  containerClass: config.containerClass,
17
+ styles: config.styles ?? [],
16
18
  vitePlugins: config.vitePlugins,
17
- includeFallbackHtml: true
19
+ includeFallbackHtml: true,
20
+ projectRoot
18
21
  })
19
22
 
20
23
  const server = await createServer({
21
24
  root: config.root,
22
25
  plugins,
23
- resolve,
24
26
  appType: 'custom',
27
+ // Aliases are scoped to the client environment only.
28
+ // The SSR environment must NOT have these aliases: they convert bare specifiers
29
+ // (e.g. 'react') into absolute paths, which bypasses Vite's fetchModule
30
+ // externalization branch and causes CJS packages to be evaluated inline by
31
+ // ESModulesEvaluator (which has no 'module'/'require' globals).
32
+ environments: {
33
+ client: {
34
+ resolve: /** @type {any} */ ({ alias })
35
+ }
36
+ },
25
37
  server: {
26
38
  port,
27
39
  strictPort: false,
package/lib/util.d.ts CHANGED
@@ -16,6 +16,11 @@ export function DefaultLayout({ title, children }: {
16
16
  title: string | undefined;
17
17
  children: import("react").ReactNode;
18
18
  }): React.JSX.Element;
19
+ /**
20
+ * @param {string} str
21
+ * @returns {string}
22
+ */
23
+ export function escapeHtml(str: string): string;
19
24
  /**
20
25
  * @param {string} cmd
21
26
  */
package/lib/util.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["util.js"],"names":[],"mappings":"AAGA,iCAA6B;AAE7B;;;;GAIG;AACH,4BAJW,MAAM,eACN,CAAG,MAAM,GAAC,MAAM,GAAA,GACd,OAAO,CASnB;AAED;;;;;;GAMG;AACH,mDAJG;IAAgC,KAAK,EAA7B,MAAM,GAAC,SAAS;IACiB,QAAQ,EAAzC,OAAO,OAAO,EAAE,SAAS;CACjC,GAAU,KAAK,CAAC,GAAG,CAAC,OAAO,CAkB7B;AAED;;GAEG;AACH,uBAFW,MAAM,gBAehB"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["util.js"],"names":[],"mappings":"AAGA,iCAA6B;AAE7B;;;;GAIG;AACH,4BAJW,MAAM,eACN,CAAG,MAAM,GAAC,MAAM,GAAA,GACd,OAAO,CASnB;AAED;;;;;;GAMG;AACH,mDAJG;IAAgC,KAAK,EAA7B,MAAM,GAAC,SAAS;IACiB,QAAQ,EAAzC,OAAO,OAAO,EAAE,SAAS;CACjC,GAAU,KAAK,CAAC,GAAG,CAAC,OAAO,CAkB7B;AAED;;;GAGG;AACH,gCAHW,MAAM,GACJ,MAAM,CAQlB;AAED;;GAEG;AACH,uBAFW,MAAM,gBAehB"}
package/lib/util.js CHANGED
@@ -42,6 +42,18 @@ export function DefaultLayout({ title, children }) {
42
42
  )
43
43
  }
44
44
 
45
+ /**
46
+ * @param {string} str
47
+ * @returns {string}
48
+ */
49
+ export function escapeHtml(str) {
50
+ return String(str)
51
+ .replace(/&/g, '&amp;')
52
+ .replace(/"/g, '&quot;')
53
+ .replace(/</g, '&lt;')
54
+ .replace(/>/g, '&gt;')
55
+ }
56
+
45
57
  /**
46
58
  * @param {string} cmd
47
59
  */
package/lib/vite.d.ts CHANGED
@@ -2,10 +2,12 @@
2
2
  * @param {Object} options
3
3
  * @param {string} options.dir
4
4
  * @param {string} [options.outDir]
5
+ * @param {string[]} [options.styles]
5
6
  * @returns {import('vite').Plugin}
6
7
  */
7
- export function pluginFactory({ dir, outDir }: {
8
+ export function pluginFactory({ dir, outDir, styles }: {
8
9
  dir: string;
9
10
  outDir?: string | undefined;
11
+ styles?: string[] | undefined;
10
12
  }): import("vite").Plugin;
11
13
  //# sourceMappingURL=vite.d.ts.map
package/lib/vite.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["vite.js"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,+CAJG;IAAwB,GAAG,EAAnB,MAAM;IACW,MAAM;CAC/B,GAAU,OAAO,MAAM,EAAE,MAAM,CA2JjC"}
1
+ {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["vite.js"],"names":[],"mappings":"AASA;;;;;;GAMG;AACH,uDALG;IAAwB,GAAG,EAAnB,MAAM;IACW,MAAM;IACJ,MAAM;CACjC,GAAU,OAAO,MAAM,EAAE,MAAM,CA8JjC"}
package/lib/vite.js CHANGED
@@ -11,9 +11,10 @@ const contentModuleIdResolved = '\0' + contentModuleId
11
11
  * @param {Object} options
12
12
  * @param {string} options.dir
13
13
  * @param {string} [options.outDir]
14
+ * @param {string[]} [options.styles]
14
15
  * @returns {import('vite').Plugin}
15
16
  */
16
- export function pluginFactory({ dir, outDir }) {
17
+ export function pluginFactory({ dir, outDir, styles = [] }) {
17
18
  const files = setup(dir, { outDir })
18
19
 
19
20
  return {
@@ -62,7 +63,10 @@ export function pluginFactory({ dir, outDir }) {
62
63
  },
63
64
  async load(id) {
64
65
  if (id === appEntryId) {
65
- return `import "orga-build/csr";`
66
+ const styleImports = styles
67
+ .map((styleUrl) => `import ${JSON.stringify(styleUrl)};`)
68
+ .join('\n')
69
+ return `${styleImports}\nimport "orga-build/csr";`
66
70
  }
67
71
  if (id === contentModuleIdResolved) {
68
72
  return await renderContentModule()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orga-build",
3
- "version": "0.5.4",
3
+ "version": "0.6.1",
4
4
  "description": "A simple tool that builds org-mode files into a website",
5
5
  "type": "module",
6
6
  "engines": {