orga-build 0.5.0 → 0.5.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 +84 -0
- package/cli.js +3 -3
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/lib/build.d.ts +2 -5
- package/lib/build.d.ts.map +1 -1
- package/lib/build.js +17 -23
- package/lib/config.d.ts +1 -2
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +30 -12
- package/lib/files.d.ts +5 -1
- package/lib/files.d.ts.map +1 -1
- package/lib/files.js +13 -4
- package/lib/plugin.d.ts +69 -0
- package/lib/plugin.d.ts.map +1 -0
- package/lib/plugin.js +138 -0
- package/lib/serve.d.ts +2 -0
- package/lib/serve.d.ts.map +1 -1
- package/lib/serve.js +23 -42
- package/lib/vite.d.ts +3 -1
- package/lib/vite.d.ts.map +1 -1
- package/lib/vite.js +3 -2
- package/package.json +1 -3
package/README.org
CHANGED
|
@@ -2,12 +2,96 @@
|
|
|
2
2
|
|
|
3
3
|
A simple tool that builds org-mode files into a website.
|
|
4
4
|
|
|
5
|
+
* Architecture
|
|
6
|
+
|
|
7
|
+
orga-build is built on top of Vite with a Vite-native architecture. The dev server uses Vite's native =createServer().listen()= pattern (no custom Express wrapper), which ensures maximum compatibility with the Vite ecosystem, including plugins like Cloudflare Workers.
|
|
8
|
+
|
|
9
|
+
** Key Design Principles
|
|
10
|
+
|
|
11
|
+
- *Vite-native*: Framework behavior is implemented as Vite plugins, not external server wrappers
|
|
12
|
+
- *Zero-config*: Works with just =.org= files - no =index.html= required
|
|
13
|
+
- *Plugin-driven*: All features are implemented as composable Vite plugins
|
|
14
|
+
|
|
15
|
+
** Plugin Composition
|
|
16
|
+
|
|
17
|
+
When using orga-build CLI with additional Vite plugins (like Cloudflare Workers), add them to =vitePlugins= in your =orga.config.js=:
|
|
18
|
+
|
|
19
|
+
#+begin_src javascript
|
|
20
|
+
// orga.config.js
|
|
21
|
+
import { cloudflare } from '@cloudflare/vite-plugin'
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
root: 'pages',
|
|
25
|
+
// Add external plugins here - orga-build plugins are included automatically
|
|
26
|
+
vitePlugins: [
|
|
27
|
+
cloudflare()
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
#+end_src
|
|
31
|
+
|
|
32
|
+
Note: Do NOT add =orgaBuildPlugin()= to =vitePlugins= - it's already included by the CLI. Only add external plugins.
|
|
33
|
+
|
|
34
|
+
For advanced users integrating directly with Vite (without the orga-build CLI), use =orgaBuildPlugin= in your =vite.config.js=:
|
|
35
|
+
|
|
36
|
+
#+begin_src javascript
|
|
37
|
+
// vite.config.js (advanced - direct Vite integration)
|
|
38
|
+
import { cloudflare } from '@cloudflare/vite-plugin'
|
|
39
|
+
import { orgaBuildPlugin, alias } from 'orga-build'
|
|
40
|
+
|
|
41
|
+
export default {
|
|
42
|
+
plugins: [
|
|
43
|
+
cloudflare(),
|
|
44
|
+
...orgaBuildPlugin({ root: 'pages', containerClass: [] })
|
|
45
|
+
],
|
|
46
|
+
resolve: { alias }
|
|
47
|
+
}
|
|
48
|
+
#+end_src
|
|
49
|
+
|
|
50
|
+
** Default HTML Template
|
|
51
|
+
|
|
52
|
+
If your project doesn't have an =index.html= file, orga-build provides a default template that:
|
|
53
|
+
- Sets up React rendering
|
|
54
|
+
- Enables client-side routing
|
|
55
|
+
- Works in both dev and production builds
|
|
56
|
+
|
|
57
|
+
To customize the HTML shell, create your own =index.html= in your project root.
|
|
58
|
+
|
|
5
59
|
* Installation
|
|
6
60
|
|
|
7
61
|
#+begin_src bash
|
|
8
62
|
npm install orga-build
|
|
9
63
|
#+end_src
|
|
10
64
|
|
|
65
|
+
* Configuration
|
|
66
|
+
|
|
67
|
+
orga-build uses =orga.config.js= (or =orga.config.mjs=) as the primary configuration file. This file should be placed in your project root.
|
|
68
|
+
|
|
69
|
+
#+begin_src javascript
|
|
70
|
+
// orga.config.js
|
|
71
|
+
export default {
|
|
72
|
+
// Directory containing your .org files (default: 'pages')
|
|
73
|
+
root: 'pages',
|
|
74
|
+
|
|
75
|
+
// Output directory for production build (default: 'out')
|
|
76
|
+
outDir: 'out',
|
|
77
|
+
|
|
78
|
+
// CSS class(es) to wrap rendered org content
|
|
79
|
+
containerClass: ['prose', 'prose-lg'],
|
|
80
|
+
|
|
81
|
+
// Additional Vite plugins
|
|
82
|
+
vitePlugins: []
|
|
83
|
+
}
|
|
84
|
+
#+end_src
|
|
85
|
+
|
|
86
|
+
** Configuration Options
|
|
87
|
+
|
|
88
|
+
| Option | Type | Default | Description |
|
|
89
|
+
|--------+------+---------+-------------|
|
|
90
|
+
| =root= | =string= | ='pages'= | Directory containing content files |
|
|
91
|
+
| =outDir= | =string= | ='out'= | Output directory for production build |
|
|
92
|
+
| =containerClass= | =string \vert string[]= | =[]= | CSS class(es) for content wrapper |
|
|
93
|
+
| =vitePlugins= | =PluginOption[]= | =[]= | Additional Vite plugins |
|
|
94
|
+
|
|
11
95
|
* TypeScript Setup
|
|
12
96
|
|
|
13
97
|
If you're using TypeScript and want type support for the =orga-build:content= virtual module, you need to add a reference to the type definitions.
|
package/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { argv
|
|
3
|
+
import { argv } from 'node:process'
|
|
4
4
|
import { parseArgs } from 'node:util'
|
|
5
5
|
import { loadConfig } from './lib/config.js'
|
|
6
6
|
import { build } from './lib/build.js'
|
|
@@ -10,12 +10,12 @@ const { positionals } = parseArgs({
|
|
|
10
10
|
args: argv.slice(2),
|
|
11
11
|
options: {
|
|
12
12
|
watch: { type: 'boolean', short: 'w' },
|
|
13
|
-
outDir: { type: 'string', short: 'o', default: 'out' }
|
|
13
|
+
outDir: { type: 'string', short: 'o', default: '.out' }
|
|
14
14
|
},
|
|
15
15
|
tokens: true,
|
|
16
16
|
allowPositionals: true
|
|
17
17
|
})
|
|
18
18
|
|
|
19
|
-
const config = await loadConfig(
|
|
19
|
+
const config = await loadConfig('orga.config.js', 'orga.config.mjs')
|
|
20
20
|
|
|
21
21
|
await (positionals.includes('dev') ? serve(config) : build(config))
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
package/lib/build.d.ts
CHANGED
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
* @param {import('./config.js').Config} config
|
|
3
3
|
*/
|
|
4
4
|
export function build({ outDir, root, containerClass, vitePlugins }: import("./config.js").Config): Promise<void>;
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
'react-dom': string;
|
|
8
|
-
wouter: string;
|
|
9
|
-
};
|
|
5
|
+
export { alias };
|
|
6
|
+
import { alias } from './plugin.js';
|
|
10
7
|
//# sourceMappingURL=build.d.ts.map
|
package/lib/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["build.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["build.js"],"names":[],"mappings":"AAcA;;GAEG;AACH,qEAFW,OAAO,aAAa,EAAE,MAAM,iBAqJtC;;sBA/J4C,aAAa"}
|
package/lib/build.js
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import { createRequire } from 'node:module'
|
|
3
2
|
import { createBuilder } from 'vite'
|
|
4
|
-
import { setupOrga } from './orga.js'
|
|
5
|
-
import react from '@vitejs/plugin-react'
|
|
6
3
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
7
|
-
import { copy, emptyDir, ensureDir } from './fs.js'
|
|
8
|
-
import { pluginFactory } from './vite.js'
|
|
4
|
+
import { copy, emptyDir, ensureDir, exists } from './fs.js'
|
|
9
5
|
import fs from 'fs/promises'
|
|
6
|
+
import { createOrgaBuildConfig, alias } from './plugin.js'
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export const alias = {
|
|
14
|
-
react: path.dirname(require.resolve('react/package.json')),
|
|
15
|
-
'react-dom': path.dirname(require.resolve('react-dom/package.json')),
|
|
16
|
-
wouter: path.dirname(require.resolve('wouter'))
|
|
17
|
-
}
|
|
8
|
+
// Re-export alias for backwards compatibility
|
|
9
|
+
export { alias }
|
|
18
10
|
|
|
19
11
|
const ssrEntry = fileURLToPath(new URL('./ssr.jsx', import.meta.url))
|
|
20
12
|
const clientEntry = fileURLToPath(new URL('./client.jsx', import.meta.url))
|
|
13
|
+
const defaultIndexHtml = fileURLToPath(new URL('./index.html', import.meta.url))
|
|
21
14
|
|
|
22
15
|
/**
|
|
23
16
|
* @param {import('./config.js').Config} config
|
|
@@ -32,18 +25,18 @@ export async function build({
|
|
|
32
25
|
const ssrOutDir = path.join(outDir, '.ssr')
|
|
33
26
|
const clientOutDir = path.join(outDir, '.client')
|
|
34
27
|
|
|
35
|
-
const plugins =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
const { plugins, resolve } = createOrgaBuildConfig({
|
|
29
|
+
root,
|
|
30
|
+
outDir,
|
|
31
|
+
containerClass,
|
|
32
|
+
vitePlugins
|
|
33
|
+
})
|
|
41
34
|
|
|
42
35
|
// Shared config with environment-specific build settings
|
|
43
36
|
const builder = await createBuilder({
|
|
44
37
|
root,
|
|
45
38
|
plugins,
|
|
46
|
-
resolve
|
|
39
|
+
resolve,
|
|
47
40
|
ssr: { noExternal: true },
|
|
48
41
|
environments: {
|
|
49
42
|
ssr: {
|
|
@@ -106,10 +99,11 @@ export async function build({
|
|
|
106
99
|
)
|
|
107
100
|
|
|
108
101
|
/* --- get html template, inject entry js and css --- */
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
)
|
|
102
|
+
// Check for user's index.html in project root, otherwise use default
|
|
103
|
+
const projectRoot = process.cwd()
|
|
104
|
+
const userIndexPath = path.join(projectRoot, 'index.html')
|
|
105
|
+
const indexHtmlPath = (await exists(userIndexPath)) ? userIndexPath : defaultIndexHtml
|
|
106
|
+
const template = await fs.readFile(indexHtmlPath, { encoding: 'utf-8' })
|
|
113
107
|
/* --- for each page path, render html using render function from ssr bundle, and inject the right css --- */
|
|
114
108
|
const pagePaths = Object.keys(pages)
|
|
115
109
|
await Promise.all(
|
package/lib/config.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @param {string} cwd
|
|
3
2
|
* @param {string[]} files
|
|
4
3
|
* @returns {Promise<Config>}
|
|
5
4
|
*/
|
|
6
|
-
export function loadConfig(
|
|
5
|
+
export function loadConfig(...files: string[]): Promise<Config>;
|
|
7
6
|
export type Config = {
|
|
8
7
|
outDir: string;
|
|
9
8
|
root: string;
|
package/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["config.js"],"names":[],"mappings":"
|
|
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"}
|
package/lib/config.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs/promises'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
import { resolvePath } from './fs.js'
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* @typedef {Object} Config
|
|
@@ -14,8 +13,8 @@ import { resolvePath } from './fs.js'
|
|
|
14
13
|
|
|
15
14
|
/** @type {Config} */
|
|
16
15
|
const defaultConfig = {
|
|
17
|
-
outDir: 'out',
|
|
18
|
-
root: '
|
|
16
|
+
outDir: '.out',
|
|
17
|
+
root: '.',
|
|
19
18
|
preBuild: [],
|
|
20
19
|
postBuild: [],
|
|
21
20
|
vitePlugins: [],
|
|
@@ -23,24 +22,43 @@ const defaultConfig = {
|
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
|
-
* @param {string} cwd
|
|
27
25
|
* @param {string[]} files
|
|
28
26
|
* @returns {Promise<Config>}
|
|
29
27
|
*/
|
|
30
|
-
export async function loadConfig(
|
|
28
|
+
export async function loadConfig(...files) {
|
|
29
|
+
const cwd = process.cwd()
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} value
|
|
33
|
+
*/
|
|
34
|
+
const resolveConfigPath = (value) =>
|
|
35
|
+
path.isAbsolute(value) ? value : path.resolve(cwd, value)
|
|
36
|
+
|
|
37
|
+
let result = { ...defaultConfig }
|
|
38
|
+
|
|
31
39
|
for (const file of files) {
|
|
32
40
|
const filePath = path.join(cwd, file)
|
|
33
41
|
|
|
34
42
|
try {
|
|
35
43
|
await fs.access(filePath, fs.constants.F_OK)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
} catch {
|
|
45
|
+
// File doesn't exist, try next
|
|
46
|
+
continue
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const module = await import(filePath)
|
|
51
|
+
// Support both default export (recommended) and named exports
|
|
52
|
+
const config = module.default || module
|
|
53
|
+
result = { ...defaultConfig, ...config }
|
|
54
|
+
break
|
|
41
55
|
} catch (err) {
|
|
42
|
-
|
|
56
|
+
// Config file exists but has errors
|
|
57
|
+
console.error(`Error loading config from ${file}:`, err)
|
|
43
58
|
}
|
|
44
59
|
}
|
|
45
|
-
|
|
60
|
+
|
|
61
|
+
result.root = resolveConfigPath(result.root)
|
|
62
|
+
result.outDir = resolveConfigPath(result.outDir)
|
|
63
|
+
return result
|
|
46
64
|
}
|
package/lib/files.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @param {string} dir
|
|
3
|
+
* @param {object} [options]
|
|
4
|
+
* @param {string} [options.outDir] - Output directory to exclude from file discovery
|
|
3
5
|
*/
|
|
4
|
-
export function setup(dir: string
|
|
6
|
+
export function setup(dir: string, { outDir }?: {
|
|
7
|
+
outDir?: string | undefined;
|
|
8
|
+
}): {
|
|
5
9
|
pages: () => Promise<Record<string, Page>>;
|
|
6
10
|
page: (id: string) => Promise<Page>;
|
|
7
11
|
components: () => Promise<string | null>;
|
package/lib/files.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["files.js"],"names":[],"mappings":"AA8EA
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["files.js"],"names":[],"mappings":"AA8EA;;;;GAIG;AACH,2BAJW,MAAM,eAEd;IAAyB,MAAM;CACjC;;eAiIY,MAAM;;;;EAKlB;;cAjNa,MAAM;;;;YACN,MAAM;;;QAMN,MAAM;UACN,MAAM;UACN,MAAM;cACN,MAAM;SACN,KAAK,GAAG,KAAK,GAAG,KAAK;UACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC"}
|
package/lib/files.js
CHANGED
|
@@ -78,8 +78,17 @@ function getContentId(slug) {
|
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
80
|
* @param {string} dir
|
|
81
|
+
* @param {object} [options]
|
|
82
|
+
* @param {string} [options.outDir] - Output directory to exclude from file discovery
|
|
81
83
|
*/
|
|
82
|
-
export function setup(dir) {
|
|
84
|
+
export function setup(dir, { outDir } = {}) {
|
|
85
|
+
const outDirRelative = outDir ? path.relative(dir, outDir) : null
|
|
86
|
+
// Only exclude outDir if it's inside the root (not an external path like ../out)
|
|
87
|
+
const outDirExclude =
|
|
88
|
+
outDirRelative && !outDirRelative.startsWith('..')
|
|
89
|
+
? `!${outDirRelative}/**`
|
|
90
|
+
: null
|
|
91
|
+
|
|
83
92
|
const pages = cache(async function () {
|
|
84
93
|
const files = await globby(
|
|
85
94
|
[
|
|
@@ -89,7 +98,7 @@ export function setup(dir) {
|
|
|
89
98
|
'!**/.*/**',
|
|
90
99
|
'!**/.*',
|
|
91
100
|
'!node_modules/**',
|
|
92
|
-
|
|
101
|
+
...(outDirExclude ? [outDirExclude] : [])
|
|
93
102
|
],
|
|
94
103
|
{ cwd: dir }
|
|
95
104
|
)
|
|
@@ -113,7 +122,7 @@ export function setup(dir) {
|
|
|
113
122
|
'!**/.*/**',
|
|
114
123
|
'!**/.*',
|
|
115
124
|
'!node_modules/**',
|
|
116
|
-
|
|
125
|
+
...(outDirExclude ? [outDirExclude] : [])
|
|
117
126
|
],
|
|
118
127
|
{
|
|
119
128
|
cwd: dir
|
|
@@ -137,7 +146,7 @@ export function setup(dir) {
|
|
|
137
146
|
'!**/.*/**',
|
|
138
147
|
'!**/.*',
|
|
139
148
|
'!node_modules/**',
|
|
140
|
-
|
|
149
|
+
...(outDirExclude ? [outDirExclude] : [])
|
|
141
150
|
],
|
|
142
151
|
{
|
|
143
152
|
cwd: dir
|
package/lib/plugin.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} OrgaBuildPluginOptions
|
|
3
|
+
* @property {string} root - Root directory for content files
|
|
4
|
+
* @property {string | undefined} [outDir] - Output directory (excluded from file discovery)
|
|
5
|
+
* @property {string|string[]} [containerClass] - CSS class(es) to wrap rendered content
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Creates the canonical orga-build plugin preset.
|
|
9
|
+
* This is the single composition path used by both dev and build.
|
|
10
|
+
*
|
|
11
|
+
* @param {OrgaBuildPluginOptions} options
|
|
12
|
+
* @returns {import('vite').PluginOption[]}
|
|
13
|
+
*/
|
|
14
|
+
export function orgaBuildPlugin({ root, outDir, containerClass }: OrgaBuildPluginOptions): import("vite").PluginOption[];
|
|
15
|
+
/**
|
|
16
|
+
* Creates the full Vite config options for orga-build.
|
|
17
|
+
* Includes plugins, resolve aliases, and other shared config.
|
|
18
|
+
*
|
|
19
|
+
* @param {OrgaBuildPluginOptions & { outDir?: string, vitePlugins?: import('vite').PluginOption[], includeFallbackHtml?: boolean, projectRoot?: string }} options
|
|
20
|
+
* @returns {{ plugins: import('vite').PluginOption[], resolve: { alias: typeof alias } }}
|
|
21
|
+
*/
|
|
22
|
+
export function createOrgaBuildConfig({ root, outDir, containerClass, vitePlugins, includeFallbackHtml, projectRoot }: OrgaBuildPluginOptions & {
|
|
23
|
+
outDir?: string;
|
|
24
|
+
vitePlugins?: import("vite").PluginOption[];
|
|
25
|
+
includeFallbackHtml?: boolean;
|
|
26
|
+
projectRoot?: string;
|
|
27
|
+
}): {
|
|
28
|
+
plugins: import("vite").PluginOption[];
|
|
29
|
+
resolve: {
|
|
30
|
+
alias: typeof alias;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Creates an HTML serving plugin that handles index.html for dev mode.
|
|
35
|
+
*
|
|
36
|
+
* This plugin:
|
|
37
|
+
* - Serves user's index.html from project root if present, otherwise uses the default template
|
|
38
|
+
* - 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
|
+
* - Does not intercept asset requests
|
|
42
|
+
*
|
|
43
|
+
* @param {string} projectRoot - Project root directory (where orga.config.js lives)
|
|
44
|
+
* @returns {import('vite').Plugin}
|
|
45
|
+
*/
|
|
46
|
+
export function htmlFallbackPlugin(projectRoot: string): import("vite").Plugin;
|
|
47
|
+
/**
|
|
48
|
+
* Alias map for React and wouter to ensure consistent resolution
|
|
49
|
+
*/
|
|
50
|
+
export const alias: {
|
|
51
|
+
react: string;
|
|
52
|
+
'react-dom': string;
|
|
53
|
+
wouter: string;
|
|
54
|
+
};
|
|
55
|
+
export type OrgaBuildPluginOptions = {
|
|
56
|
+
/**
|
|
57
|
+
* - Root directory for content files
|
|
58
|
+
*/
|
|
59
|
+
root: string;
|
|
60
|
+
/**
|
|
61
|
+
* - Output directory (excluded from file discovery)
|
|
62
|
+
*/
|
|
63
|
+
outDir?: string | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* - CSS class(es) to wrap rendered content
|
|
66
|
+
*/
|
|
67
|
+
containerClass?: string | string[];
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +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,CAIzC;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;AA9HD;;GAEG;AACH;;;;EAIC;;;;;UAIa,MAAM;;;;aACN,MAAM,GAAG,SAAS;;;;qBAClB,MAAM,GAAC,MAAM,EAAE"}
|
package/lib/plugin.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import fs from 'node:fs/promises'
|
|
3
|
+
import { createRequire } from 'node:module'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import react from '@vitejs/plugin-react'
|
|
6
|
+
import { setupOrga } from './orga.js'
|
|
7
|
+
import { pluginFactory } from './vite.js'
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url)
|
|
10
|
+
const defaultIndexHtml = fileURLToPath(new URL('./index.html', import.meta.url))
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Alias map for React and wouter to ensure consistent resolution
|
|
14
|
+
*/
|
|
15
|
+
export const alias = {
|
|
16
|
+
react: path.dirname(require.resolve('react/package.json')),
|
|
17
|
+
'react-dom': path.dirname(require.resolve('react-dom/package.json')),
|
|
18
|
+
wouter: path.dirname(require.resolve('wouter'))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {Object} OrgaBuildPluginOptions
|
|
23
|
+
* @property {string} root - Root directory for content files
|
|
24
|
+
* @property {string | undefined} [outDir] - Output directory (excluded from file discovery)
|
|
25
|
+
* @property {string|string[]} [containerClass] - CSS class(es) to wrap rendered content
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates the canonical orga-build plugin preset.
|
|
30
|
+
* This is the single composition path used by both dev and build.
|
|
31
|
+
*
|
|
32
|
+
* @param {OrgaBuildPluginOptions} options
|
|
33
|
+
* @returns {import('vite').PluginOption[]}
|
|
34
|
+
*/
|
|
35
|
+
export function orgaBuildPlugin({ root, outDir, containerClass = [] }) {
|
|
36
|
+
return [setupOrga({ containerClass }), react(), pluginFactory({ dir: root, outDir })]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Creates the full Vite config options for orga-build.
|
|
41
|
+
* Includes plugins, resolve aliases, and other shared config.
|
|
42
|
+
*
|
|
43
|
+
* @param {OrgaBuildPluginOptions & { outDir?: string, vitePlugins?: import('vite').PluginOption[], includeFallbackHtml?: boolean, projectRoot?: string }} options
|
|
44
|
+
* @returns {{ plugins: import('vite').PluginOption[], resolve: { alias: typeof alias } }}
|
|
45
|
+
*/
|
|
46
|
+
export function createOrgaBuildConfig({
|
|
47
|
+
root,
|
|
48
|
+
outDir,
|
|
49
|
+
containerClass = [],
|
|
50
|
+
vitePlugins = [],
|
|
51
|
+
includeFallbackHtml = false,
|
|
52
|
+
projectRoot = process.cwd()
|
|
53
|
+
}) {
|
|
54
|
+
const plugins = [...vitePlugins, ...orgaBuildPlugin({ root, outDir, containerClass })]
|
|
55
|
+
if (includeFallbackHtml) {
|
|
56
|
+
// HTML fallback must be first so it can handle HTML navigation requests
|
|
57
|
+
// before runtime plugins (e.g. Cloudflare) potentially return 404.
|
|
58
|
+
plugins.unshift(htmlFallbackPlugin(projectRoot))
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
plugins,
|
|
62
|
+
resolve: { alias }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Checks if a user-provided index.html exists in the project root.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} root - Project root directory
|
|
70
|
+
* @returns {Promise<boolean>}
|
|
71
|
+
*/
|
|
72
|
+
async function hasUserIndexHtml(root) {
|
|
73
|
+
try {
|
|
74
|
+
await fs.access(path.join(root, 'index.html'), fs.constants.F_OK)
|
|
75
|
+
return true
|
|
76
|
+
} catch {
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Creates an HTML serving plugin that handles index.html for dev mode.
|
|
83
|
+
*
|
|
84
|
+
* This plugin:
|
|
85
|
+
* - Serves user's index.html from project root if present, otherwise uses the default template
|
|
86
|
+
* - Only handles GET/HEAD requests that accept HTML
|
|
87
|
+
* - Runs late (post middleware) so other plugins get first chance
|
|
88
|
+
* - Passes HTML through transformIndexHtml for ecosystem compatibility
|
|
89
|
+
* - Does not intercept asset requests
|
|
90
|
+
*
|
|
91
|
+
* @param {string} projectRoot - Project root directory (where orga.config.js lives)
|
|
92
|
+
* @returns {import('vite').Plugin}
|
|
93
|
+
*/
|
|
94
|
+
export function htmlFallbackPlugin(projectRoot) {
|
|
95
|
+
return {
|
|
96
|
+
name: 'orga-build:html-fallback',
|
|
97
|
+
|
|
98
|
+
async configureServer(server) {
|
|
99
|
+
// Determine which index.html to use at startup
|
|
100
|
+
// Look for user's index.html in project root (where orga.config.js lives)
|
|
101
|
+
const userIndexPath = path.join(projectRoot, 'index.html')
|
|
102
|
+
const userHasIndex = await hasUserIndexHtml(projectRoot)
|
|
103
|
+
const indexHtmlPath = userHasIndex ? userIndexPath : defaultIndexHtml
|
|
104
|
+
|
|
105
|
+
// Add middleware to serve HTML early in the chain.
|
|
106
|
+
server.middlewares.use(async (req, res, next) => {
|
|
107
|
+
// Only handle GET/HEAD requests
|
|
108
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
109
|
+
return next()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Only handle browser-like navigation requests.
|
|
113
|
+
// Don't match generic */* accepts to avoid hijacking API requests.
|
|
114
|
+
const accept = req.headers.accept || ''
|
|
115
|
+
if (!accept.includes('text/html')) {
|
|
116
|
+
return next()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Don't intercept asset requests (files with extensions)
|
|
120
|
+
const url = req.url || '/'
|
|
121
|
+
const pathname = url.split('?')[0]
|
|
122
|
+
if (pathname !== '/' && /\.\w+$/.test(pathname)) {
|
|
123
|
+
return next()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
let html = await fs.readFile(indexHtmlPath, 'utf-8')
|
|
128
|
+
html = await server.transformIndexHtml(url, html)
|
|
129
|
+
res.statusCode = 200
|
|
130
|
+
res.setHeader('Content-Type', 'text/html')
|
|
131
|
+
res.end(html)
|
|
132
|
+
} catch (e) {
|
|
133
|
+
next(e)
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
package/lib/serve.d.ts
CHANGED
package/lib/serve.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["serve.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["serve.js"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,8BAHW,OAAO,aAAa,EAAE,MAAM,SAC5B,MAAM,iBA2BhB"}
|
package/lib/serve.js
CHANGED
|
@@ -1,55 +1,36 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path from 'node:path'
|
|
2
2
|
import { createServer } from 'vite'
|
|
3
|
-
import
|
|
4
|
-
import react from '@vitejs/plugin-react'
|
|
5
|
-
import { pluginFactory } from './vite.js'
|
|
6
|
-
import { alias } from './build.js'
|
|
7
|
-
import { setupOrga } from './orga.js'
|
|
3
|
+
import { createOrgaBuildConfig } from './plugin.js'
|
|
8
4
|
|
|
9
5
|
/**
|
|
6
|
+
* Start the development server using native Vite.
|
|
7
|
+
*
|
|
10
8
|
* @param {import('./config.js').Config} config
|
|
11
9
|
* @param {number} [port]
|
|
12
10
|
*/
|
|
13
11
|
export async function serve(config, port = 3000) {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
...config.vitePlugins
|
|
21
|
-
],
|
|
22
|
-
server: { middlewareMode: true },
|
|
23
|
-
appType: 'custom',
|
|
24
|
-
resolve: {
|
|
25
|
-
alias: alias
|
|
26
|
-
}
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
app.use(vite.middlewares)
|
|
30
|
-
app.get('/favicon.ico', (req, res) => {
|
|
31
|
-
res.status(404).end()
|
|
12
|
+
const { plugins, resolve } = createOrgaBuildConfig({
|
|
13
|
+
root: config.root,
|
|
14
|
+
outDir: config.outDir,
|
|
15
|
+
containerClass: config.containerClass,
|
|
16
|
+
vitePlugins: config.vitePlugins,
|
|
17
|
+
includeFallbackHtml: true
|
|
32
18
|
})
|
|
33
19
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const html = template
|
|
46
|
-
res.status(200).setHeader('Content-Type', 'text/html').end(html)
|
|
47
|
-
} catch (/** @type{any} */ e) {
|
|
48
|
-
next(e)
|
|
20
|
+
const server = await createServer({
|
|
21
|
+
root: config.root,
|
|
22
|
+
plugins,
|
|
23
|
+
resolve,
|
|
24
|
+
appType: 'custom',
|
|
25
|
+
server: {
|
|
26
|
+
port,
|
|
27
|
+
strictPort: false,
|
|
28
|
+
watch: {
|
|
29
|
+
ignored: [path.resolve(config.outDir) + '/**']
|
|
30
|
+
}
|
|
49
31
|
}
|
|
50
32
|
})
|
|
51
33
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
})
|
|
34
|
+
await server.listen()
|
|
35
|
+
server.printUrls()
|
|
55
36
|
}
|
package/lib/vite.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @param {Object} options
|
|
3
3
|
* @param {string} options.dir
|
|
4
|
+
* @param {string} [options.outDir]
|
|
4
5
|
* @returns {import('vite').Plugin}
|
|
5
6
|
*/
|
|
6
|
-
export function pluginFactory({ dir }: {
|
|
7
|
+
export function pluginFactory({ dir, outDir }: {
|
|
7
8
|
dir: string;
|
|
9
|
+
outDir?: string | undefined;
|
|
8
10
|
}): import("vite").Plugin;
|
|
9
11
|
//# 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
|
|
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"}
|
package/lib/vite.js
CHANGED
|
@@ -10,10 +10,11 @@ const contentModuleIdResolved = '\0' + contentModuleId
|
|
|
10
10
|
/**
|
|
11
11
|
* @param {Object} options
|
|
12
12
|
* @param {string} options.dir
|
|
13
|
+
* @param {string} [options.outDir]
|
|
13
14
|
* @returns {import('vite').Plugin}
|
|
14
15
|
*/
|
|
15
|
-
export function pluginFactory({ dir }) {
|
|
16
|
-
const files = setup(dir)
|
|
16
|
+
export function pluginFactory({ dir, outDir }) {
|
|
17
|
+
const files = setup(dir, { outDir })
|
|
17
18
|
|
|
18
19
|
return {
|
|
19
20
|
name: 'vite-plugin-orga-pages',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orga-build",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "A simple tool that builds org-mode files into a website",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -44,7 +44,6 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@vitejs/plugin-react": "^5.1.4",
|
|
47
|
-
"express": "^5.1.0",
|
|
48
47
|
"globby": "^14.1.0",
|
|
49
48
|
"react": "^19.0.0",
|
|
50
49
|
"react-dom": "^19.0.0",
|
|
@@ -55,7 +54,6 @@
|
|
|
55
54
|
"@orgajs/rollup": "1.3.3"
|
|
56
55
|
},
|
|
57
56
|
"devDependencies": {
|
|
58
|
-
"@types/express": "^5.0.1",
|
|
59
57
|
"@types/hast": "^3.0.4",
|
|
60
58
|
"@types/node": "^22.13.1",
|
|
61
59
|
"@types/react": "^19.0.8",
|