@davaux/multisite 0.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/CLAUDE.md +133 -0
- package/README.md +147 -0
- package/package.json +42 -0
- package/src/build.ts +183 -0
- package/src/index.ts +1219 -0
- package/src/test/fixtures/base/routes/_layout.ts +6 -0
- package/src/test/fixtures/base/routes/about.page.ts +3 -0
- package/src/test/fixtures/base/routes/index.page.ts +3 -0
- package/src/test/fixtures/site-a/routes/_layout.ts +6 -0
- package/src/test/fixtures/site-a/routes/_middleware.ts +6 -0
- package/src/test/fixtures/site-a/routes/config.page.ts +7 -0
- package/src/test/fixtures/site-a/routes/index.page.ts +3 -0
- package/src/test/fixtures/site-a/routes/shop.page.ts +3 -0
- package/src/test/fixtures/site-a/routes/state.page.ts +3 -0
- package/src/test/fixtures/site-b/routes/_error.ts +3 -0
- package/src/test/fixtures/site-b/routes/about.page.ts +3 -0
- package/src/test/multisite.test.ts +650 -0
- package/tsconfig.json +17 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<!-- pka-generated -->
|
|
2
|
+
# @davaux/multisite
|
|
3
|
+
|
|
4
|
+
> Generated by Project Knowledge Analyzer on 2026-06-06T21:53:10.370Z
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
Multi-site hosting for Davaux — run multiple sites from a single codebase
|
|
9
|
+
|
|
10
|
+
**Version**: 0.8.0
|
|
11
|
+
**Author**: David L Dyess II
|
|
12
|
+
**License**: MIT
|
|
13
|
+
**Repository**: https://codeberg.org/davaux/davaux#readme
|
|
14
|
+
|
|
15
|
+
## Tech Stack
|
|
16
|
+
|
|
17
|
+
- **Language**: TypeScript
|
|
18
|
+
- **Module System**: ESM (`type: module`)
|
|
19
|
+
- **Build Tool**: esbuild
|
|
20
|
+
|
|
21
|
+
## Commands
|
|
22
|
+
|
|
23
|
+
- `npm run build` — tsc
|
|
24
|
+
- `npm run typecheck` — tsc --noEmit
|
|
25
|
+
- `npm run test` — node --import tsx/esm --test 'src/test/**/*.test.ts'
|
|
26
|
+
|
|
27
|
+
## Project Structure
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
├── README.md
|
|
31
|
+
├── package.json
|
|
32
|
+
├── tsconfig.json
|
|
33
|
+
└── src/
|
|
34
|
+
├── test/
|
|
35
|
+
│ ├── fixtures/
|
|
36
|
+
│ │ ├── base/
|
|
37
|
+
│ │ │ └── routes/
|
|
38
|
+
│ │ │ ├── _layout.ts
|
|
39
|
+
│ │ │ ├── about.page.ts
|
|
40
|
+
│ │ │ └── index.page.ts
|
|
41
|
+
│ │ ├── site-a/
|
|
42
|
+
│ │ │ └── routes/
|
|
43
|
+
│ │ │ ├── _layout.ts
|
|
44
|
+
│ │ │ ├── _middleware.ts
|
|
45
|
+
│ │ │ ├── config.page.ts
|
|
46
|
+
│ │ │ ├── index.page.ts
|
|
47
|
+
│ │ │ ├── shop.page.ts
|
|
48
|
+
│ │ │ └── state.page.ts
|
|
49
|
+
│ │ └── site-b/
|
|
50
|
+
│ │ └── routes/
|
|
51
|
+
│ │ ├── _error.ts
|
|
52
|
+
│ │ └── about.page.ts
|
|
53
|
+
│ └── multisite.test.ts
|
|
54
|
+
└── index.ts
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Entry Points
|
|
59
|
+
|
|
60
|
+
- `src/index.ts`
|
|
61
|
+
|
|
62
|
+
## Files by Type
|
|
63
|
+
|
|
64
|
+
### Documentation (1)
|
|
65
|
+
- `README.md`
|
|
66
|
+
|
|
67
|
+
### Config (2)
|
|
68
|
+
- `package.json`
|
|
69
|
+
- `tsconfig.json`
|
|
70
|
+
|
|
71
|
+
### Module (1)
|
|
72
|
+
- `src/index.ts`
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## Git
|
|
76
|
+
- **Branch**: main
|
|
77
|
+
- **Last Commit**: chore: Add alpha status note to README
|
|
78
|
+
- **Author**: David Dyess II
|
|
79
|
+
- **Date**: 2026-06-06 15:52:27 -0600
|
|
80
|
+
- **Remote**: https://codeberg.org/davaux/davaux.git
|
|
81
|
+
|
|
82
|
+
### Recent Commits
|
|
83
|
+
```
|
|
84
|
+
f527031 chore: Add alpha status note to README
|
|
85
|
+
90c819e chore: Add repo info to package.json files
|
|
86
|
+
b200d9d feat(davaux)!: Add OmlCacheConfig - opt-in with includes option or opt-out with excludes option; Fix OML implementation to follow OML spec - use output instead of return
|
|
87
|
+
3bce0c2 chore: Update and add READMEs to packages; update ROADMAP
|
|
88
|
+
fc27c20 fix(davaux): Remove old dist folder on new builds; fix server port per DavauxConfig
|
|
89
|
+
c760659 feat(davaux): Add minify CSS in production builds
|
|
90
|
+
c899ab8 fix(davaux): Added method JS and JSX extenstion to scanner
|
|
91
|
+
7d99f04 feat(davaux): Add support for declarative partial updates
|
|
92
|
+
3b47f37 chore: Bump package versions to 0.8.0
|
|
93
|
+
aa0460f chore: Add CHANGELOG.md
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Dependencies
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
### Development
|
|
100
|
+
@types/node, davaux, esbuild, tsx, typescript
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
## Tests (12)
|
|
107
|
+
|
|
108
|
+
### Test Suites (1)
|
|
109
|
+
- `src/test/multisite.test.ts`
|
|
110
|
+
|
|
111
|
+
### Fixtures (11)
|
|
112
|
+
- `src/test/fixtures/base/routes/_layout.ts`
|
|
113
|
+
- `src/test/fixtures/base/routes/about.page.ts`
|
|
114
|
+
- `src/test/fixtures/base/routes/index.page.ts`
|
|
115
|
+
- `src/test/fixtures/site-a/routes/_layout.ts`
|
|
116
|
+
- `src/test/fixtures/site-a/routes/_middleware.ts`
|
|
117
|
+
- `src/test/fixtures/site-a/routes/config.page.ts`
|
|
118
|
+
- `src/test/fixtures/site-a/routes/index.page.ts`
|
|
119
|
+
- `src/test/fixtures/site-a/routes/shop.page.ts`
|
|
120
|
+
- `src/test/fixtures/site-a/routes/state.page.ts`
|
|
121
|
+
- `src/test/fixtures/site-b/routes/_error.ts`
|
|
122
|
+
- `src/test/fixtures/site-b/routes/about.page.ts`
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
## Import Graph
|
|
127
|
+
|
|
128
|
+
## Exported Symbols
|
|
129
|
+
|
|
130
|
+
**`src/index.ts`**
|
|
131
|
+
`mergeScanResults`, `defineSites`, `buildMultisiteApps`, `startMultisiteServer`, `dispatchToSite`, `startMultisiteDev`, `startMultisite`, `getSite`, `SiteDefinition`, `MultisiteConfig`, `BuildOptions`, `ServerOptions`, `StartMultisiteOptions`
|
|
132
|
+
|
|
133
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# @davaux/multisite
|
|
2
|
+
|
|
3
|
+
Multi-site hosting for Davaux — run multiple sites from a single process, dispatched by `Host` header.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @davaux/multisite
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Basic setup
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// server.ts
|
|
15
|
+
import { startMultisite } from '@davaux/multisite'
|
|
16
|
+
|
|
17
|
+
startMultisite({
|
|
18
|
+
sites: [
|
|
19
|
+
{
|
|
20
|
+
name: 'main',
|
|
21
|
+
hostname: 'example.com',
|
|
22
|
+
routesDir: './src/routes/main',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'blog',
|
|
26
|
+
hostname: 'blog.example.com',
|
|
27
|
+
routesDir: './src/routes/blog',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
}, { port: 3000, cwd: import.meta.dirname })
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`startMultisite` auto-detects `NODE_ENV` — dev mode with file watching and live reload when `NODE_ENV !== 'production'`, production dispatch otherwise. Pass `cwd: import.meta.dirname` from your `server.ts` for reliable path resolution.
|
|
34
|
+
|
|
35
|
+
## Shared base + per-site overlay
|
|
36
|
+
|
|
37
|
+
Use `baseDir` for routes shared across all sites. Each site's `routesDir` overlays on top — when both define the same URL pattern and type, the site-specific route wins:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
startMultisite({
|
|
41
|
+
baseDir: './src/routes/base',
|
|
42
|
+
islandsDir: './src/islands/base',
|
|
43
|
+
publicDir: './public',
|
|
44
|
+
sites: [
|
|
45
|
+
{ name: 'tenant-a', hostname: 'a.example.com', routesDir: './src/routes/tenant-a' },
|
|
46
|
+
{ name: 'tenant-b', hostname: 'b.example.com', routesDir: './src/routes/tenant-b' },
|
|
47
|
+
{ name: 'fallback', hostname: '*' },
|
|
48
|
+
],
|
|
49
|
+
}, { cwd: import.meta.dirname })
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`'*'` as `hostname` is a catch-all for any host not explicitly registered.
|
|
53
|
+
|
|
54
|
+
## Per-site config
|
|
55
|
+
|
|
56
|
+
Attach arbitrary data to each site via `SiteDefinition.config`. Access it in any handler, layout, or middleware via `getSite<T>(ctx)`:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { getSite } from '@davaux/multisite'
|
|
60
|
+
|
|
61
|
+
// In server.ts:
|
|
62
|
+
startMultisite({
|
|
63
|
+
sites: [
|
|
64
|
+
{ name: 'acme', hostname: 'acme.example.com', config: { theme: 'blue', name: 'Acme' } },
|
|
65
|
+
{ name: 'globex', hostname: 'globex.example.com', config: { theme: 'red', name: 'Globex' } },
|
|
66
|
+
],
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// In any route file:
|
|
70
|
+
export default definePage((ctx) => {
|
|
71
|
+
const site = getSite<{ theme: string; name: string }>(ctx)
|
|
72
|
+
return <h1>Welcome to {site?.name}</h1>
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`getSite` returns `undefined` when called outside a multisite server (e.g. in tests).
|
|
77
|
+
|
|
78
|
+
## `SiteDefinition` options
|
|
79
|
+
|
|
80
|
+
| Option | Type | Description |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `name` | `string` | Unique site identifier used in logging and asset paths |
|
|
83
|
+
| `hostname` | `string \| string[]` | `Host` header value(s) that route to this site. `'*'` is a catch-all |
|
|
84
|
+
| `routesDir` | `string?` | Site-specific routes directory, overlaid on `baseDir` |
|
|
85
|
+
| `islandsDir` | `string?` | Site-specific islands directory, merged with shared `islandsDir` |
|
|
86
|
+
| `publicDir` | `string?` | Site-specific static files directory (takes priority over shared `publicDir`) |
|
|
87
|
+
| `clientEntry` | `string?` | Site-specific client bundle entry. Overrides shared `clientEntry` |
|
|
88
|
+
| `config` | `T?` | Arbitrary per-site data, accessible via `getSite<T>(ctx)` |
|
|
89
|
+
|
|
90
|
+
## `MultisiteConfig` options
|
|
91
|
+
|
|
92
|
+
| Option | Type | Description |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `sites` | `SiteDefinition[]` | Site definitions (required) |
|
|
95
|
+
| `baseDir` | `string?` | Shared base routes directory |
|
|
96
|
+
| `islandsDir` | `string?` | Shared islands directory — included in every site's client bundle |
|
|
97
|
+
| `publicDir` | `string?` | Shared static files directory |
|
|
98
|
+
| `clientEntry` | `string?` | Shared client bundle entry compiled to `/_davaux/client.js` |
|
|
99
|
+
|
|
100
|
+
## Production build
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// build.ts
|
|
104
|
+
import { buildMultisite } from '@davaux/multisite/build'
|
|
105
|
+
|
|
106
|
+
await buildMultisite({
|
|
107
|
+
sites: [
|
|
108
|
+
{ name: 'main', hostname: 'example.com', routesDir: './src/routes/main' },
|
|
109
|
+
],
|
|
110
|
+
}, { cwd: import.meta.dirname })
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Compiles all route files, per-site island bundles, and `server.ts` / `multisite.config.ts` to `dist/`. Run with `node dist/server.js`.
|
|
114
|
+
|
|
115
|
+
## Advanced: embedding in a custom server
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { buildMultisiteApps, dispatchToSite } from '@davaux/multisite'
|
|
119
|
+
import { createServer } from 'node:http'
|
|
120
|
+
|
|
121
|
+
const apps = await buildMultisiteApps(config, { cwd: import.meta.dirname })
|
|
122
|
+
|
|
123
|
+
const server = createServer(async (req, res) => {
|
|
124
|
+
const handled = await dispatchToSite(apps, req, res)
|
|
125
|
+
if (!handled) { res.writeHead(404); res.end('No site') }
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## TypeScript
|
|
130
|
+
|
|
131
|
+
Use `defineSites<T>` to get type inference on `SiteDefinition.config` without a type annotation at every call site:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { defineSites } from '@davaux/multisite'
|
|
135
|
+
|
|
136
|
+
interface SiteConfig { theme: string }
|
|
137
|
+
|
|
138
|
+
export const sites = defineSites<SiteConfig>({
|
|
139
|
+
sites: [
|
|
140
|
+
{ name: 'acme', hostname: 'acme.example.com', config: { theme: 'blue' } },
|
|
141
|
+
],
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Documentation
|
|
146
|
+
|
|
147
|
+
[davaux.codeberg.page/docs/multisite](https://davaux.codeberg.page/docs/multisite)
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@davaux/multisite",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "Multi-site hosting for Davaux — run multiple sites from a single codebase",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"author": "David L Dyess II",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://codeberg.org/davaux/davaux"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://codeberg.org/davaux/davaux/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://codeberg.org/davaux/davaux#readme",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./build": {
|
|
22
|
+
"import": "./dist/build.js",
|
|
23
|
+
"types": "./dist/build.d.ts"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"test": "node --import tsx/esm --test 'src/test/**/*.test.ts'"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"davaux": ">=0.8.0",
|
|
33
|
+
"esbuild": ">=0.17.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^25.0.0",
|
|
37
|
+
"davaux": "*",
|
|
38
|
+
"esbuild": "^0.28.0",
|
|
39
|
+
"tsx": "^4.22.3",
|
|
40
|
+
"typescript": "^6.0.3"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/build.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { join, resolve } from 'node:path'
|
|
3
|
+
import { collectCss, generateIslandsEntry, islandServerPlugin } from 'davaux/build'
|
|
4
|
+
import {
|
|
5
|
+
collectEsbuildPlugins,
|
|
6
|
+
collectScannerSuffixes,
|
|
7
|
+
type DavauxPlugin,
|
|
8
|
+
pathsToAlias,
|
|
9
|
+
} from 'davaux/config'
|
|
10
|
+
import { scanIslands, scanRoutes } from 'davaux/scanner'
|
|
11
|
+
import type { MultisiteConfig } from './index.js'
|
|
12
|
+
|
|
13
|
+
export interface BuildMultisiteOptions {
|
|
14
|
+
/** Project root directory. Defaults to `process.cwd()`. */
|
|
15
|
+
cwd?: string
|
|
16
|
+
/** Output directory. Defaults to `{cwd}/dist`. */
|
|
17
|
+
outDir?: string
|
|
18
|
+
/** Extra packages to mark as external in addition to `node:*`, `davaux`, and `@davaux/multisite`. */
|
|
19
|
+
external?: string[]
|
|
20
|
+
/**
|
|
21
|
+
* Davaux plugins to apply during the build — contributes esbuild transforms and scanner
|
|
22
|
+
* suffix extensions. Use the same plugins here as in your `davaux.config.ts`.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* import { mdx } from '@davaux/mdx'
|
|
26
|
+
* await buildMultisite(sites, { cwd: import.meta.dirname, plugins: [mdx()] })
|
|
27
|
+
*/
|
|
28
|
+
plugins?: DavauxPlugin[]
|
|
29
|
+
/**
|
|
30
|
+
* tsconfig-style path aliases forwarded to esbuild `alias`. Same format as
|
|
31
|
+
* `compilerOptions.paths` — e.g. `{ '~/*': ['./src/*'] }`.
|
|
32
|
+
*/
|
|
33
|
+
paths?: Record<string, string>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Compile a multisite project to JavaScript.
|
|
38
|
+
*
|
|
39
|
+
* Reads route and island directories directly from the `MultisiteConfig` so the
|
|
40
|
+
* config is the single source of truth. Discovers and compiles all route, layout,
|
|
41
|
+
* middleware, and error-page files, per-site client island bundles, plus
|
|
42
|
+
* `server.ts` / `multisite.config.ts` if they exist at the project root.
|
|
43
|
+
*
|
|
44
|
+
* Requires `esbuild` to be installed (it is present in any project that
|
|
45
|
+
* depends on `davaux`).
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* // build.ts
|
|
49
|
+
* import { buildMultisite } from '@davaux/multisite/build'
|
|
50
|
+
* import { sites } from './multisite.config.js'
|
|
51
|
+
*
|
|
52
|
+
* await buildMultisite(sites, { cwd: import.meta.dirname })
|
|
53
|
+
*/
|
|
54
|
+
export async function buildMultisite<T>(
|
|
55
|
+
config: MultisiteConfig<T>,
|
|
56
|
+
options: BuildMultisiteOptions = {},
|
|
57
|
+
): Promise<void> {
|
|
58
|
+
const { build } = await import('esbuild')
|
|
59
|
+
const cwd = options.cwd ?? process.cwd()
|
|
60
|
+
const outDir = options.outDir ?? resolve(cwd, 'dist')
|
|
61
|
+
const davauxPlugins = options.plugins ?? []
|
|
62
|
+
const extraSuffixes = [...(config.extraSuffixes ?? []), ...collectScannerSuffixes(davauxPlugins)]
|
|
63
|
+
const userAlias = pathsToAlias(options.paths ?? {})
|
|
64
|
+
|
|
65
|
+
// All island directories (for the server-side island plugin)
|
|
66
|
+
const allIslandDirs: string[] = [
|
|
67
|
+
...(config.islandsDir ? [config.islandsDir] : []),
|
|
68
|
+
...config.sites.flatMap((s) => (s.islandsDir ? [s.islandsDir] : [])),
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
// Collect route directories directly from config — no re-discovery needed
|
|
72
|
+
const routeDirs: string[] = []
|
|
73
|
+
if (config.baseDir) routeDirs.push(config.baseDir)
|
|
74
|
+
for (const site of config.sites) {
|
|
75
|
+
if (site.routesDir) routeDirs.push(site.routesDir)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Scan for compilable files in each routes directory
|
|
79
|
+
const routeFiles: string[] = []
|
|
80
|
+
for (const dir of routeDirs) {
|
|
81
|
+
if (!existsSync(dir)) continue
|
|
82
|
+
const { routes, layouts, middlewares, errorPage } = await scanRoutes(dir, extraSuffixes)
|
|
83
|
+
routeFiles.push(
|
|
84
|
+
...routes.map((r) => r.filePath),
|
|
85
|
+
...layouts.map((l) => l.filePath),
|
|
86
|
+
...middlewares.map((m) => m.filePath),
|
|
87
|
+
...(errorPage ? [errorPage] : []),
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Include server.ts and multisite.config.ts at the project root if present
|
|
92
|
+
const entryPoints: string[] = [...routeFiles]
|
|
93
|
+
for (const name of ['server.ts', 'multisite.config.ts']) {
|
|
94
|
+
const p = resolve(cwd, name)
|
|
95
|
+
if (existsSync(p)) entryPoints.push(p)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const baseExternal = ['node:*', 'davaux', '@davaux/multisite', ...(options.external ?? [])]
|
|
99
|
+
|
|
100
|
+
// Include src/middleware.ts if present (app-level middleware runs before route matching)
|
|
101
|
+
const middlewareSrc = resolve(cwd, 'src', 'middleware.ts')
|
|
102
|
+
if (existsSync(middlewareSrc)) entryPoints.push(middlewareSrc)
|
|
103
|
+
|
|
104
|
+
// Compile routes (with island server wrapping + plugin transforms)
|
|
105
|
+
await build({
|
|
106
|
+
entryPoints,
|
|
107
|
+
outdir: outDir,
|
|
108
|
+
outbase: cwd,
|
|
109
|
+
format: 'esm',
|
|
110
|
+
platform: 'node',
|
|
111
|
+
target: 'node22',
|
|
112
|
+
bundle: true,
|
|
113
|
+
jsx: 'automatic',
|
|
114
|
+
jsxImportSource: 'davaux',
|
|
115
|
+
external: baseExternal,
|
|
116
|
+
sourcemap: true,
|
|
117
|
+
alias: { ...userAlias, 'davaux/client': 'davaux/signal' },
|
|
118
|
+
plugins: [
|
|
119
|
+
...(allIslandDirs.length > 0 ? [islandServerPlugin(allIslandDirs)] : []),
|
|
120
|
+
...collectEsbuildPlugins(davauxPlugins),
|
|
121
|
+
],
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// Collect CSS side-effect outputs into dist/_davaux/styles.css
|
|
125
|
+
const stylesOutFile = join(outDir, '_davaux', 'styles.css')
|
|
126
|
+
await collectCss(outDir, stylesOutFile)
|
|
127
|
+
|
|
128
|
+
// Compile per-site client island bundles to dist/_davaux/<name>/islands.js
|
|
129
|
+
const baseIslands = config.islandsDir ? await scanIslands(config.islandsDir) : []
|
|
130
|
+
let islandBundles = 0
|
|
131
|
+
|
|
132
|
+
for (const site of config.sites) {
|
|
133
|
+
const siteIslands = site.islandsDir ? await scanIslands(site.islandsDir) : []
|
|
134
|
+
const allIslands = [...baseIslands, ...siteIslands]
|
|
135
|
+
if (allIslands.length === 0) continue
|
|
136
|
+
|
|
137
|
+
await build({
|
|
138
|
+
stdin: {
|
|
139
|
+
contents: generateIslandsEntry(allIslands),
|
|
140
|
+
loader: 'ts',
|
|
141
|
+
resolveDir: cwd,
|
|
142
|
+
},
|
|
143
|
+
outfile: join(outDir, '_davaux', site.name, 'islands.js'),
|
|
144
|
+
format: 'esm',
|
|
145
|
+
platform: 'browser',
|
|
146
|
+
target: 'es2022',
|
|
147
|
+
bundle: true,
|
|
148
|
+
jsx: 'automatic',
|
|
149
|
+
jsxImportSource: 'davaux/client',
|
|
150
|
+
sourcemap: true,
|
|
151
|
+
alias: userAlias,
|
|
152
|
+
plugins: collectEsbuildPlugins(davauxPlugins),
|
|
153
|
+
})
|
|
154
|
+
islandBundles++
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Compile per-site client bundles to dist/_davaux/<name>/client.js
|
|
158
|
+
let clientBundles = 0
|
|
159
|
+
|
|
160
|
+
for (const site of config.sites) {
|
|
161
|
+
const clientEntry = site.clientEntry ?? config.clientEntry
|
|
162
|
+
if (!clientEntry || !existsSync(clientEntry)) continue
|
|
163
|
+
|
|
164
|
+
await build({
|
|
165
|
+
entryPoints: [clientEntry],
|
|
166
|
+
outfile: join(outDir, '_davaux', site.name, 'client.js'),
|
|
167
|
+
format: 'esm',
|
|
168
|
+
platform: 'browser',
|
|
169
|
+
target: 'es2022',
|
|
170
|
+
bundle: true,
|
|
171
|
+
jsx: 'automatic',
|
|
172
|
+
jsxImportSource: 'davaux/client',
|
|
173
|
+
sourcemap: true,
|
|
174
|
+
alias: userAlias,
|
|
175
|
+
plugins: collectEsbuildPlugins(davauxPlugins),
|
|
176
|
+
})
|
|
177
|
+
clientBundles++
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log(
|
|
181
|
+
`[davaux/multisite] Built ${routeFiles.length} route file(s), ${islandBundles} island bundle(s), ${clientBundles} client bundle(s)`,
|
|
182
|
+
)
|
|
183
|
+
}
|