@eighty4/dank 0.0.4-1 → 0.0.4-3
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/client/client.js +1 -0
- package/lib/bin.ts +8 -11
- package/lib/build.ts +41 -70
- package/lib/build_tag.ts +3 -3
- package/lib/config.ts +372 -11
- package/lib/dank.ts +21 -150
- package/lib/define.ts +6 -4
- package/lib/developer.ts +146 -0
- package/lib/dirs.ts +83 -0
- package/lib/errors.ts +6 -0
- package/lib/esbuild.ts +19 -29
- package/lib/flags.ts +15 -121
- package/lib/html.ts +196 -112
- package/lib/http.ts +59 -43
- package/lib/public.ts +10 -10
- package/lib/{metadata.ts → registry.ts} +216 -83
- package/lib/serve.ts +118 -270
- package/lib/services.ts +8 -8
- package/lib/watch.ts +39 -0
- package/lib_js/bin.js +79 -85
- package/lib_js/build.js +63 -86
- package/lib_js/build_tag.js +20 -21
- package/lib_js/config.js +237 -18
- package/lib_js/dank.js +5 -122
- package/lib_js/define.js +8 -5
- package/lib_js/dirs.js +61 -0
- package/lib_js/errors.js +9 -0
- package/lib_js/esbuild.js +155 -167
- package/lib_js/flags.js +30 -123
- package/lib_js/html.js +280 -231
- package/lib_js/http.js +176 -195
- package/lib_js/public.js +45 -46
- package/lib_js/registry.js +260 -0
- package/lib_js/serve.js +109 -251
- package/lib_js/services.js +152 -171
- package/lib_js/watch.js +35 -0
- package/lib_types/dank.d.ts +13 -1
- package/package.json +10 -4
- package/client/esbuild.js +0 -91
- package/lib_js/metadata.js +0 -210
package/lib/http.ts
CHANGED
|
@@ -10,8 +10,14 @@ import {
|
|
|
10
10
|
import { extname, join } from 'node:path'
|
|
11
11
|
import { Readable } from 'node:stream'
|
|
12
12
|
import mime from 'mime'
|
|
13
|
-
import type {
|
|
14
|
-
import type {
|
|
13
|
+
import type { DankDirectories } from './dirs.ts'
|
|
14
|
+
import type { DankFlags } from './flags.ts'
|
|
15
|
+
import type {
|
|
16
|
+
UrlRewrite,
|
|
17
|
+
UrlRewriteProvider,
|
|
18
|
+
WebsiteManifest,
|
|
19
|
+
WebsiteRegistry,
|
|
20
|
+
} from './registry.ts'
|
|
15
21
|
import type { HttpServices } from './services.ts'
|
|
16
22
|
|
|
17
23
|
export type FrontendFetcher = (
|
|
@@ -21,25 +27,15 @@ export type FrontendFetcher = (
|
|
|
21
27
|
notFound: () => void,
|
|
22
28
|
) => void
|
|
23
29
|
|
|
24
|
-
// state needed to eval url rewriting after FrontendFetcher and before HttpServices
|
|
25
|
-
export type PageRouteState = {
|
|
26
|
-
// urls of html entrypoints
|
|
27
|
-
urls: Array<string>
|
|
28
|
-
urlRewrites: Array<UrlRewrite>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type UrlRewrite = {
|
|
32
|
-
pattern: RegExp
|
|
33
|
-
url: string
|
|
34
|
-
}
|
|
35
|
-
|
|
36
30
|
export function startWebServer(
|
|
37
|
-
|
|
31
|
+
port: number,
|
|
32
|
+
flags: DankFlags,
|
|
33
|
+
dirs: DankDirectories,
|
|
34
|
+
urlRewriteProvider: UrlRewriteProvider,
|
|
38
35
|
frontendFetcher: FrontendFetcher,
|
|
39
36
|
httpServices: HttpServices,
|
|
40
|
-
pageRoutes: PageRouteState,
|
|
41
37
|
) {
|
|
42
|
-
const serverAddress = 'http://localhost:' +
|
|
38
|
+
const serverAddress = 'http://localhost:' + port
|
|
43
39
|
const handler = (req: IncomingMessage, res: ServerResponse) => {
|
|
44
40
|
if (!req.url || !req.method) {
|
|
45
41
|
res.end()
|
|
@@ -52,19 +48,20 @@ export function startWebServer(
|
|
|
52
48
|
url,
|
|
53
49
|
headers,
|
|
54
50
|
httpServices,
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
flags,
|
|
52
|
+
dirs,
|
|
53
|
+
urlRewriteProvider,
|
|
57
54
|
res,
|
|
58
55
|
),
|
|
59
56
|
)
|
|
60
57
|
}
|
|
61
58
|
}
|
|
62
|
-
createServer(
|
|
63
|
-
|
|
59
|
+
createServer(flags.logHttp ? createLogWrapper(handler) : handler).listen(
|
|
60
|
+
port,
|
|
64
61
|
)
|
|
65
62
|
console.log(
|
|
66
|
-
|
|
67
|
-
`server is live at http://127.0.0.1:${
|
|
63
|
+
flags.preview ? 'preview' : 'dev',
|
|
64
|
+
`server is live at http://127.0.0.1:${port}`,
|
|
68
65
|
)
|
|
69
66
|
}
|
|
70
67
|
|
|
@@ -73,12 +70,18 @@ async function onNotFound(
|
|
|
73
70
|
url: URL,
|
|
74
71
|
headers: Headers,
|
|
75
72
|
httpServices: HttpServices,
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
flags: DankFlags,
|
|
74
|
+
dirs: DankDirectories,
|
|
75
|
+
urlRewriteProvider: UrlRewriteProvider,
|
|
78
76
|
res: ServerResponse,
|
|
79
77
|
) {
|
|
80
78
|
if (req.method === 'GET' && extname(url.pathname) === '') {
|
|
81
|
-
const urlRewrite = tryUrlRewrites(
|
|
79
|
+
const urlRewrite = tryUrlRewrites(
|
|
80
|
+
flags,
|
|
81
|
+
dirs,
|
|
82
|
+
urlRewriteProvider.urlRewrites,
|
|
83
|
+
url,
|
|
84
|
+
)
|
|
82
85
|
if (urlRewrite) {
|
|
83
86
|
streamFile(urlRewrite, res)
|
|
84
87
|
return
|
|
@@ -107,15 +110,20 @@ async function sendFetchResponse(res: ServerResponse, fetchResponse: Response) {
|
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
function tryUrlRewrites(
|
|
113
|
+
flags: DankFlags,
|
|
114
|
+
dirs: DankDirectories,
|
|
115
|
+
urlRewrites: Array<UrlRewrite>,
|
|
110
116
|
url: URL,
|
|
111
|
-
pageRoutes: PageRouteState,
|
|
112
|
-
serve: DankServe,
|
|
113
117
|
): string | null {
|
|
114
|
-
const urlRewrite =
|
|
118
|
+
const urlRewrite = urlRewrites.find(urlRewrite =>
|
|
115
119
|
urlRewrite.pattern.test(url.pathname),
|
|
116
120
|
)
|
|
117
121
|
return urlRewrite
|
|
118
|
-
? join(
|
|
122
|
+
? join(
|
|
123
|
+
flags.preview ? dirs.buildDist : dirs.buildWatch,
|
|
124
|
+
urlRewrite.url,
|
|
125
|
+
'index.html',
|
|
126
|
+
)
|
|
119
127
|
: null
|
|
120
128
|
}
|
|
121
129
|
|
|
@@ -178,7 +186,7 @@ function createLogWrapper(handler: RequestListener): RequestListener {
|
|
|
178
186
|
}
|
|
179
187
|
|
|
180
188
|
export function createBuiltDistFilesFetcher(
|
|
181
|
-
|
|
189
|
+
dirs: DankDirectories,
|
|
182
190
|
manifest: WebsiteManifest,
|
|
183
191
|
): FrontendFetcher {
|
|
184
192
|
return (
|
|
@@ -188,34 +196,42 @@ export function createBuiltDistFilesFetcher(
|
|
|
188
196
|
notFound: () => void,
|
|
189
197
|
) => {
|
|
190
198
|
if (manifest.pageUrls.has(url.pathname)) {
|
|
191
|
-
streamFile(
|
|
199
|
+
streamFile(
|
|
200
|
+
join(
|
|
201
|
+
dirs.projectResolved,
|
|
202
|
+
dirs.buildDist,
|
|
203
|
+
url.pathname,
|
|
204
|
+
'index.html',
|
|
205
|
+
),
|
|
206
|
+
res,
|
|
207
|
+
)
|
|
192
208
|
} else if (manifest.files.has(url.pathname)) {
|
|
193
|
-
streamFile(
|
|
209
|
+
streamFile(
|
|
210
|
+
join(dirs.projectResolved, dirs.buildDist, url.pathname),
|
|
211
|
+
res,
|
|
212
|
+
)
|
|
194
213
|
} else {
|
|
195
214
|
notFound()
|
|
196
215
|
}
|
|
197
216
|
}
|
|
198
217
|
}
|
|
199
218
|
|
|
200
|
-
// todo replace PageRouteState with WebsiteRegistry
|
|
201
219
|
export function createDevServeFilesFetcher(
|
|
202
|
-
|
|
203
|
-
|
|
220
|
+
esbuildPort: number,
|
|
221
|
+
dirs: DankDirectories,
|
|
222
|
+
registry: WebsiteRegistry,
|
|
204
223
|
): FrontendFetcher {
|
|
205
|
-
const proxyAddress = 'http://127.0.0.1:' +
|
|
224
|
+
const proxyAddress = 'http://127.0.0.1:' + esbuildPort
|
|
206
225
|
return (
|
|
207
226
|
url: URL,
|
|
208
227
|
_headers: Headers,
|
|
209
228
|
res: ServerResponse,
|
|
210
229
|
notFound: () => void,
|
|
211
230
|
) => {
|
|
212
|
-
if (
|
|
213
|
-
streamFile(
|
|
214
|
-
join(serve.dirs.buildWatch, url.pathname, 'index.html'),
|
|
215
|
-
res,
|
|
216
|
-
)
|
|
231
|
+
if (registry.pageUrls.includes(url.pathname)) {
|
|
232
|
+
streamFile(join(dirs.buildWatch, url.pathname, 'index.html'), res)
|
|
217
233
|
} else {
|
|
218
|
-
const maybePublicPath = join(
|
|
234
|
+
const maybePublicPath = join(dirs.public, url.pathname)
|
|
219
235
|
exists(maybePublicPath).then(fromPublic => {
|
|
220
236
|
if (fromPublic) {
|
|
221
237
|
streamFile(maybePublicPath, res)
|
package/lib/public.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { copyFile, mkdir, readdir, stat } from 'node:fs/promises'
|
|
2
2
|
import { platform } from 'node:os'
|
|
3
3
|
import { join } from 'node:path'
|
|
4
|
-
import type {
|
|
4
|
+
import type { DankDirectories } from './dirs.ts'
|
|
5
5
|
|
|
6
6
|
export async function copyAssets(
|
|
7
|
-
|
|
7
|
+
dirs: DankDirectories,
|
|
8
8
|
): Promise<Array<string> | null> {
|
|
9
9
|
try {
|
|
10
|
-
const stats = await stat(
|
|
10
|
+
const stats = await stat(dirs.public)
|
|
11
11
|
if (stats.isDirectory()) {
|
|
12
|
-
await mkdir(
|
|
13
|
-
return await recursiveCopyAssets(
|
|
12
|
+
await mkdir(dirs.buildDist, { recursive: true })
|
|
13
|
+
return await recursiveCopyAssets(dirs)
|
|
14
14
|
} else {
|
|
15
15
|
throw Error('./public cannot be a file')
|
|
16
16
|
}
|
|
@@ -22,13 +22,13 @@ export async function copyAssets(
|
|
|
22
22
|
const IGNORE = platform() === 'darwin' ? ['.DS_Store'] : []
|
|
23
23
|
|
|
24
24
|
async function recursiveCopyAssets(
|
|
25
|
-
|
|
25
|
+
dirs: DankDirectories,
|
|
26
26
|
dir: string = '',
|
|
27
27
|
): Promise<Array<string>> {
|
|
28
28
|
const copied: Array<string> = []
|
|
29
|
-
const to = join(
|
|
29
|
+
const to = join(dirs.buildDist, dir)
|
|
30
30
|
let madeDir = dir === ''
|
|
31
|
-
const listingDir = join(
|
|
31
|
+
const listingDir = join(dirs.public, dir)
|
|
32
32
|
for (const p of await readdir(listingDir)) {
|
|
33
33
|
if (IGNORE.includes(p)) {
|
|
34
34
|
continue
|
|
@@ -36,10 +36,10 @@ async function recursiveCopyAssets(
|
|
|
36
36
|
try {
|
|
37
37
|
const stats = await stat(join(listingDir, p))
|
|
38
38
|
if (stats.isDirectory()) {
|
|
39
|
-
copied.push(...(await recursiveCopyAssets(
|
|
39
|
+
copied.push(...(await recursiveCopyAssets(dirs, join(dir, p))))
|
|
40
40
|
} else {
|
|
41
41
|
if (!madeDir) {
|
|
42
|
-
await mkdir(join(
|
|
42
|
+
await mkdir(join(dirs.buildDist, dir), {
|
|
43
43
|
recursive: true,
|
|
44
44
|
})
|
|
45
45
|
madeDir = true
|
|
@@ -1,52 +1,13 @@
|
|
|
1
1
|
import EventEmitter from 'node:events'
|
|
2
2
|
import { writeFile } from 'node:fs/promises'
|
|
3
|
-
import {
|
|
3
|
+
import { join } from 'node:path'
|
|
4
4
|
import type { BuildResult } from 'esbuild'
|
|
5
|
+
import type { ResolvedDankConfig } from './config.ts'
|
|
6
|
+
import { LOG } from './developer.ts'
|
|
7
|
+
import { Resolver, type DankDirectories } from './dirs.ts'
|
|
5
8
|
import type { EntryPoint } from './esbuild.ts'
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
export type ResolveError = 'outofbounds'
|
|
9
|
-
|
|
10
|
-
export type Resolver = {
|
|
11
|
-
// `p` is expected to be a relative path resolvable from the project dir
|
|
12
|
-
isProjectSubpathInPagesDir(p: string): boolean
|
|
13
|
-
|
|
14
|
-
// `p` is expected to be a relative path resolvable from the pages dir
|
|
15
|
-
isPagesSubpathInPagesDir(p: string): boolean
|
|
16
|
-
|
|
17
|
-
// resolve a pages subpath from a resource within the pages directory by a relative href
|
|
18
|
-
// `from` is expected to be a pages resource fs path starting with `pages/` and ending with filename
|
|
19
|
-
// the result will be a pages subpath and will not have the pages dir prefix
|
|
20
|
-
// returns 'outofbounds' if the relative path does not resolve to a file within the pages dir
|
|
21
|
-
resolveHrefInPagesDir(from: string, href: string): string | ResolveError
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
class ResolverImpl implements Resolver {
|
|
25
|
-
#dirs: ProjectDirs
|
|
26
|
-
|
|
27
|
-
constructor(dirs: ProjectDirs) {
|
|
28
|
-
this.#dirs = dirs
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
isProjectSubpathInPagesDir(p: string): boolean {
|
|
32
|
-
return resolve(join(this.#dirs.projectResolved, p)).startsWith(
|
|
33
|
-
this.#dirs.pagesResolved,
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
isPagesSubpathInPagesDir(p: string): boolean {
|
|
38
|
-
return this.isProjectSubpathInPagesDir(join(this.#dirs.pages, p))
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
resolveHrefInPagesDir(from: string, href: string): string | ResolveError {
|
|
42
|
-
const p = join(dirname(from), href)
|
|
43
|
-
if (this.isProjectSubpathInPagesDir(p)) {
|
|
44
|
-
return p
|
|
45
|
-
} else {
|
|
46
|
-
return 'outofbounds'
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
9
|
+
import { HtmlEntrypoint } from './html.ts'
|
|
10
|
+
import type { PageMapping } from './dank.ts'
|
|
50
11
|
|
|
51
12
|
// summary of a website build
|
|
52
13
|
export type WebsiteManifest = {
|
|
@@ -75,31 +36,55 @@ type WorkerManifest = {
|
|
|
75
36
|
// path to bundled entrypoint dependent on `clientScript`
|
|
76
37
|
dependentEntryPoint: string
|
|
77
38
|
workerEntryPoint: string
|
|
39
|
+
workerCtor: 'Worker' | 'SharedWorker'
|
|
78
40
|
workerUrl: string
|
|
79
41
|
workerUrlPlaceholder: string
|
|
80
42
|
}
|
|
81
43
|
|
|
82
44
|
export type WebsiteRegistryEvents = {
|
|
45
|
+
entrypoints: []
|
|
46
|
+
webpage: [entrypoint: HtmlEntrypoint]
|
|
83
47
|
workers: []
|
|
84
48
|
}
|
|
85
49
|
|
|
50
|
+
type WebpageRegistration = {
|
|
51
|
+
pageUrl: `/${string}`
|
|
52
|
+
fsPath: string
|
|
53
|
+
html: HtmlEntrypoint
|
|
54
|
+
bundles: Array<EntryPoint>
|
|
55
|
+
urlRewrite?: UrlRewrite
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type UrlRewrite = {
|
|
59
|
+
pattern: RegExp
|
|
60
|
+
url: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type UrlRewriteProvider = {
|
|
64
|
+
urlRewrites: Array<UrlRewrite>
|
|
65
|
+
}
|
|
66
|
+
|
|
86
67
|
// manages website resources during `dank build` and `dank serve`
|
|
87
68
|
export class WebsiteRegistry extends EventEmitter<WebsiteRegistryEvents> {
|
|
88
|
-
|
|
89
|
-
// paths of bundled esbuild outputs
|
|
69
|
+
// paths of bundled esbuild outputs, as built by esbuild
|
|
90
70
|
#bundles: Set<string> = new Set()
|
|
71
|
+
#c: ResolvedDankConfig
|
|
91
72
|
// public dir assets
|
|
92
73
|
#copiedAssets: Set<string> | null = null
|
|
93
74
|
// map of entrypoints to their output path
|
|
94
75
|
#entrypointHrefs: Record<string, string | null> = {}
|
|
95
|
-
#
|
|
96
|
-
#resolver: Resolver
|
|
76
|
+
#pages: Record<`/${string}`, WebpageRegistration> = {}
|
|
77
|
+
readonly #resolver: Resolver
|
|
97
78
|
#workers: Array<WorkerManifest> | null = null
|
|
98
79
|
|
|
99
|
-
constructor(
|
|
80
|
+
constructor(config: ResolvedDankConfig) {
|
|
100
81
|
super()
|
|
101
|
-
this.#
|
|
102
|
-
this.#resolver = new
|
|
82
|
+
this.#c = config
|
|
83
|
+
this.#resolver = new Resolver(config.dirs)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get config(): ResolvedDankConfig {
|
|
87
|
+
return this.#c
|
|
103
88
|
}
|
|
104
89
|
|
|
105
90
|
set copiedAssets(copiedAssets: Array<string> | null) {
|
|
@@ -107,29 +92,88 @@ export class WebsiteRegistry extends EventEmitter<WebsiteRegistryEvents> {
|
|
|
107
92
|
copiedAssets === null ? null : new Set(copiedAssets)
|
|
108
93
|
}
|
|
109
94
|
|
|
110
|
-
|
|
111
|
-
this.#
|
|
95
|
+
get htmlEntrypoints(): Array<HtmlEntrypoint> {
|
|
96
|
+
return Object.values(this.#pages).map(p => p.html)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
get pageUrls(): Array<string> {
|
|
100
|
+
return Object.keys(this.#pages)
|
|
112
101
|
}
|
|
113
102
|
|
|
114
103
|
get resolver(): Resolver {
|
|
115
104
|
return this.#resolver
|
|
116
105
|
}
|
|
117
106
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
107
|
+
get urlRewrites(): Array<UrlRewrite> {
|
|
108
|
+
return Object.values(this.#pages)
|
|
109
|
+
.filter(
|
|
110
|
+
(pr): pr is WebpageRegistration & { urlRewrite: UrlRewrite } =>
|
|
111
|
+
typeof pr.urlRewrite !== 'undefined',
|
|
112
|
+
)
|
|
113
|
+
.map(pr => pr.urlRewrite)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
get webpageEntryPoints(): Array<EntryPoint> {
|
|
117
|
+
const unique: Set<EntryPoint['in']> = new Set()
|
|
118
|
+
return Object.values(this.#pages)
|
|
119
|
+
.flatMap(p => p.bundles)
|
|
120
|
+
.filter(entryPoint => {
|
|
121
|
+
if (unique.has(entryPoint.in)) {
|
|
122
|
+
return false
|
|
123
|
+
} else {
|
|
124
|
+
unique.add(entryPoint.in)
|
|
125
|
+
return true
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
get webpageAndWorkerEntryPoints(): Array<EntryPoint> {
|
|
131
|
+
const unique: Set<EntryPoint['in']> = new Set()
|
|
132
|
+
const pageBundles = Object.values(this.#pages).flatMap(p => p.bundles)
|
|
133
|
+
const workerBundles = this.workerEntryPoints
|
|
134
|
+
const bundles = workerBundles
|
|
135
|
+
? [...pageBundles, ...workerBundles]
|
|
136
|
+
: pageBundles
|
|
137
|
+
return bundles.filter(entryPoint => {
|
|
138
|
+
if (unique.has(entryPoint.in)) {
|
|
139
|
+
return false
|
|
140
|
+
} else {
|
|
141
|
+
unique.add(entryPoint.in)
|
|
142
|
+
return true
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
get workerEntryPoints(): Array<EntryPoint> | null {
|
|
148
|
+
return (
|
|
149
|
+
this.#workers?.map(({ workerEntryPoint }) => ({
|
|
150
|
+
in: workerEntryPoint,
|
|
151
|
+
out: workerEntryPoint
|
|
152
|
+
.replace(/^pages[\//]/, '')
|
|
153
|
+
.replace(/\.(mj|t)s$/, '.js'),
|
|
154
|
+
})) || null
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
get workers(): Array<WorkerManifest> | null {
|
|
159
|
+
return this.#workers
|
|
160
|
+
}
|
|
125
161
|
|
|
126
162
|
buildRegistry(): BuildRegistry {
|
|
127
|
-
return new BuildRegistry(
|
|
163
|
+
return new BuildRegistry(
|
|
164
|
+
this.#c.dirs,
|
|
165
|
+
this.#resolver,
|
|
166
|
+
this.#onBuildManifest,
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
configSync() {
|
|
171
|
+
this.#configDiff()
|
|
128
172
|
}
|
|
129
173
|
|
|
130
174
|
files(): Set<string> {
|
|
131
175
|
const files = new Set<string>()
|
|
132
|
-
for (const pageUrl of this.#
|
|
176
|
+
for (const pageUrl of Object.keys(this.#pages))
|
|
133
177
|
files.add(pageUrl === '/' ? '/index.html' : `${pageUrl}/index.html`)
|
|
134
178
|
for (const f of this.#bundles) files.add(f)
|
|
135
179
|
if (this.#copiedAssets) for (const f of this.#copiedAssets) files.add(f)
|
|
@@ -145,27 +189,12 @@ export class WebsiteRegistry extends EventEmitter<WebsiteRegistryEvents> {
|
|
|
145
189
|
}
|
|
146
190
|
}
|
|
147
191
|
|
|
148
|
-
workerEntryPoints(): Array<EntryPoint> | null {
|
|
149
|
-
return (
|
|
150
|
-
this.#workers?.map(({ workerEntryPoint }) => ({
|
|
151
|
-
in: workerEntryPoint,
|
|
152
|
-
out: workerEntryPoint
|
|
153
|
-
.replace(/^pages[\//]/, '')
|
|
154
|
-
.replace(/\.(mj|t)s$/, '.js'),
|
|
155
|
-
})) || null
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
workers(): Array<WorkerManifest> | null {
|
|
160
|
-
return this.#workers
|
|
161
|
-
}
|
|
162
|
-
|
|
163
192
|
async writeManifest(buildTag: string): Promise<WebsiteManifest> {
|
|
164
193
|
const manifest = this.#manifest(buildTag)
|
|
165
194
|
await writeFile(
|
|
166
195
|
join(
|
|
167
|
-
this.#
|
|
168
|
-
this.#
|
|
196
|
+
this.#c.dirs.projectRootAbs,
|
|
197
|
+
this.#c.dirs.buildRoot,
|
|
169
198
|
'website.json',
|
|
170
199
|
),
|
|
171
200
|
JSON.stringify(
|
|
@@ -181,11 +210,103 @@ export class WebsiteRegistry extends EventEmitter<WebsiteRegistryEvents> {
|
|
|
181
210
|
return manifest
|
|
182
211
|
}
|
|
183
212
|
|
|
213
|
+
#configDiff() {
|
|
214
|
+
const updatePages: ResolvedDankConfig['pages'] = this.#c.devPages
|
|
215
|
+
? { ...this.#c.pages, ...this.#c.devPages }
|
|
216
|
+
: { ...this.#c.pages }
|
|
217
|
+
const prevPages = new Set(Object.keys(this.#pages))
|
|
218
|
+
for (const [urlPath, mapping] of Object.entries(updatePages)) {
|
|
219
|
+
const existingPage = prevPages.delete(urlPath as `/${string}`)
|
|
220
|
+
if (existingPage) {
|
|
221
|
+
this.#configPageUpdate(urlPath as `/${string}`, mapping)
|
|
222
|
+
} else {
|
|
223
|
+
this.#configPageAdd(urlPath as `/${string}`, mapping)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
for (const prevPage of prevPages) {
|
|
227
|
+
this.#configPageRemove(prevPage as `/${string}`)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
#configPageAdd(urlPath: `/${string}`, mapping: PageMapping) {
|
|
232
|
+
LOG({
|
|
233
|
+
realm: 'registry',
|
|
234
|
+
message: 'added page',
|
|
235
|
+
data: {
|
|
236
|
+
urlPath,
|
|
237
|
+
srcPath: mapping.webpage,
|
|
238
|
+
},
|
|
239
|
+
})
|
|
240
|
+
const html = new HtmlEntrypoint(
|
|
241
|
+
this.#c,
|
|
242
|
+
this.#resolver,
|
|
243
|
+
urlPath as `/${string}`,
|
|
244
|
+
mapping.webpage,
|
|
245
|
+
)
|
|
246
|
+
const urlRewrite = mapping.pattern
|
|
247
|
+
? { pattern: mapping.pattern, url: urlPath }
|
|
248
|
+
: undefined
|
|
249
|
+
this.#pages[urlPath as `/${string}`] = {
|
|
250
|
+
pageUrl: urlPath as `/${string}`,
|
|
251
|
+
fsPath: mapping.webpage,
|
|
252
|
+
html,
|
|
253
|
+
urlRewrite,
|
|
254
|
+
bundles: [],
|
|
255
|
+
}
|
|
256
|
+
html.on('entrypoints', entrypoints =>
|
|
257
|
+
this.#setWebpageBundles(html.url, entrypoints),
|
|
258
|
+
)
|
|
259
|
+
this.emit('webpage', html)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
#configPageUpdate(urlPath: `/${string}`, mapping: PageMapping) {
|
|
263
|
+
const existingRegistration = this.#pages[urlPath as `/${string}`]
|
|
264
|
+
if (existingRegistration.fsPath !== mapping.webpage) {
|
|
265
|
+
this.#configPageRemove(urlPath)
|
|
266
|
+
this.#configPageAdd(urlPath, mapping)
|
|
267
|
+
} else if (
|
|
268
|
+
existingRegistration.urlRewrite?.pattern.source !==
|
|
269
|
+
mapping.pattern?.source
|
|
270
|
+
) {
|
|
271
|
+
if (mapping.pattern) {
|
|
272
|
+
existingRegistration.urlRewrite = {
|
|
273
|
+
url: urlPath,
|
|
274
|
+
pattern: mapping.pattern,
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
existingRegistration.urlRewrite = undefined
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
LOG({
|
|
281
|
+
realm: 'registry',
|
|
282
|
+
message: 'updated page src',
|
|
283
|
+
data: {
|
|
284
|
+
urlPath,
|
|
285
|
+
newSrcPath: mapping.webpage,
|
|
286
|
+
oldSrcPath: this.#pages[urlPath as `/${string}`].fsPath,
|
|
287
|
+
},
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
#configPageRemove(urlPath: `/${string}`) {
|
|
292
|
+
const registration = this.#pages[urlPath]
|
|
293
|
+
LOG({
|
|
294
|
+
realm: 'registry',
|
|
295
|
+
message: 'removed page',
|
|
296
|
+
data: {
|
|
297
|
+
urlPath,
|
|
298
|
+
srcPath: registration.fsPath,
|
|
299
|
+
},
|
|
300
|
+
})
|
|
301
|
+
registration.html.removeAllListeners()
|
|
302
|
+
delete this.#pages[urlPath]
|
|
303
|
+
}
|
|
304
|
+
|
|
184
305
|
#manifest(buildTag: string): WebsiteManifest {
|
|
185
306
|
return {
|
|
186
307
|
buildTag,
|
|
187
308
|
files: this.files(),
|
|
188
|
-
pageUrls: new Set(this.#
|
|
309
|
+
pageUrls: new Set(Object.keys(this.#pages)),
|
|
189
310
|
}
|
|
190
311
|
}
|
|
191
312
|
|
|
@@ -246,20 +367,32 @@ export class WebsiteRegistry extends EventEmitter<WebsiteRegistryEvents> {
|
|
|
246
367
|
this.emit('workers')
|
|
247
368
|
}
|
|
248
369
|
}
|
|
370
|
+
|
|
371
|
+
#setWebpageBundles(url: `/${string}`, bundles: Array<EntryPoint>) {
|
|
372
|
+
this.#pages[url].bundles = bundles
|
|
373
|
+
this.emit('entrypoints')
|
|
374
|
+
}
|
|
249
375
|
}
|
|
250
376
|
|
|
251
377
|
// result accumulator of an esbuild `build` or `Context.rebuild`
|
|
252
378
|
export class BuildRegistry {
|
|
379
|
+
#dirs: DankDirectories
|
|
253
380
|
#onComplete: OnBuildComplete
|
|
254
381
|
#resolver: Resolver
|
|
255
382
|
#workers: Array<Omit<WorkerManifest, 'dependentEntryPoint'>> | null = null
|
|
256
383
|
|
|
257
384
|
constructor(
|
|
258
|
-
|
|
385
|
+
dirs: DankDirectories,
|
|
386
|
+
resolver: Resolver,
|
|
259
387
|
onComplete: (manifest: BuildManifest) => void,
|
|
260
388
|
) {
|
|
389
|
+
this.#dirs = dirs
|
|
261
390
|
this.#onComplete = onComplete
|
|
262
|
-
this.#resolver =
|
|
391
|
+
this.#resolver = resolver
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
get dirs(): DankDirectories {
|
|
395
|
+
return this.#dirs
|
|
263
396
|
}
|
|
264
397
|
|
|
265
398
|
get resolver(): Resolver {
|