@eighty4/dank 0.0.4-2 → 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/lib/html.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'node:events'
2
2
  import { readFile } from 'node:fs/promises'
3
- import { dirname, join, relative } from 'node:path'
3
+ import { dirname, isAbsolute, join, relative, resolve } from 'node:path'
4
4
  import { extname } from 'node:path/posix'
5
5
  import {
6
6
  defaultTreeAdapter,
@@ -9,9 +9,11 @@ import {
9
9
  parseFragment,
10
10
  serialize,
11
11
  } from 'parse5'
12
+ import type { ResolvedDankConfig } from './config.ts'
13
+ import { LOG } from './developer.ts'
14
+ import type { Resolver } from './dirs.ts'
15
+ import { DankError } from './errors.ts'
12
16
  import type { EntryPoint } from './esbuild.ts'
13
- import type { DankBuild } from './flags.ts'
14
- import type { Resolver } from './metadata.ts'
15
17
 
16
18
  type CommentNode = DefaultTreeAdapterTypes.CommentNode
17
19
  type Document = DefaultTreeAdapterTypes.Document
@@ -28,6 +30,8 @@ type PartialReference = {
28
30
  commentNode: CommentNode
29
31
  // path within pages dir omitting pages/ segment
30
32
  fsPath: string
33
+ // unresolved partial path read from commentNode text
34
+ specifier: string
31
35
  }
32
36
 
33
37
  type PartialContent = PartialReference & {
@@ -44,7 +48,7 @@ type ImportedScript = {
44
48
  entrypoint: EntryPoint
45
49
  }
46
50
 
47
- export type HtmlDecoration = {
51
+ type HtmlDecoration = {
48
52
  type: 'script'
49
53
  js: string
50
54
  }
@@ -61,86 +65,103 @@ export type HtmlEntrypointEvents = {
61
65
  // Dispatched from HtmlEntrypoint to notify `dank serve` of changes to esbuild entrypoints
62
66
  // Parameter `entrypoints` is the esbuild mappings of the input and output paths
63
67
  entrypoints: [entrypoints: Array<EntryPoint>]
68
+ // Dispatched from HtmlEntrypoint when processing HTML is aborted on error
69
+ error: [e: Error]
64
70
  // Dispatched from HtmlEntrypoint to notify when new HtmlEntrypoint.#document output is ready for write
65
71
  // Parameter `html` is the updated html content of the page ready to be output to the build dir
66
72
  output: [html: string]
67
- // Dispatched from HtmlEntrypoint to notify `dank serve` of a partial dependency for an HtmlEntrypoint
68
- // Seemingly a duplicate of event `partials` but it keeps relevant state in sync during async io
69
- // Parameter `partial` is the fs path to the partial
70
- partial: [partial: string]
71
- // Dispatched from HtmlEntrypoint to notify `dank serve` of completely resolved imported partials
72
- // Parameter `partials` are the fs paths to the partials
73
- partials: [partials: Array<string>]
74
73
  }
75
74
 
76
75
  export class HtmlEntrypoint extends EventEmitter<HtmlEntrypointEvents> {
77
- #build: DankBuild
78
- #decorations?: Array<HtmlDecoration>
76
+ #c: ResolvedDankConfig
77
+ #clientJS: ClientJS | null
79
78
  #document: Document = defaultTreeAdapter.createDocument()
80
- // todo cache entrypoints set for quicker diffing
81
- // #entrypoints: Set<string> = new Set()
79
+ #entrypoints: Set<string> = new Set()
82
80
  // path within pages dir omitting pages/ segment
83
81
  #fsPath: string
84
82
  #partials: Array<PartialContent> = []
85
83
  #resolver: Resolver
86
84
  #scripts: Array<ImportedScript> = []
87
85
  #update: Object = Object()
88
- #url: string
86
+ #url: `/${string}`
89
87
 
90
88
  constructor(
91
- build: DankBuild,
89
+ c: ResolvedDankConfig,
92
90
  resolver: Resolver,
93
- url: string,
91
+ url: `/${string}`,
94
92
  fsPath: string,
95
- decorations?: Array<HtmlDecoration>,
96
93
  ) {
97
94
  super({ captureRejections: true })
98
- this.#build = build
95
+ this.#c = c
96
+ this.#clientJS = ClientJS.initialize(c)
99
97
  this.#resolver = resolver
100
- this.#decorations = decorations
101
98
  this.#url = url
102
99
  this.#fsPath = fsPath
103
100
  this.on('change', this.#onChange)
104
- this.emit('change')
105
101
  }
106
102
 
107
103
  get fsPath(): string {
108
104
  return this.#fsPath
109
105
  }
110
106
 
111
- get url(): string {
107
+ get url(): `/${string}` {
112
108
  return this.#url
113
109
  }
114
110
 
115
- async #html(): Promise<string> {
116
- try {
117
- return await readFile(
118
- join(this.#build.dirs.pages, this.#fsPath),
119
- 'utf8',
120
- )
121
- } catch (e) {
122
- // todo error handling
123
- errorExit(this.#fsPath + ' does not exist')
124
- }
111
+ async process() {
112
+ await this.#onChange()
113
+ }
114
+
115
+ output(hrefs?: HtmlHrefs): string {
116
+ this.#injectPartials()
117
+ this.#rewriteHrefs(hrefs)
118
+ return serialize(this.#document)
119
+ }
120
+
121
+ usesPartial(fsPath: string): boolean {
122
+ return this.#partials.some(partial => partial.fsPath === fsPath)
125
123
  }
126
124
 
127
- // todo if partial changes, hot swap content in page
128
- #onChange = async (_partial?: string) => {
125
+ #onChange = async () => {
129
126
  const update = (this.#update = Object())
130
- const html = await this.#html()
127
+ let html: string
128
+ try {
129
+ html = await this.#readFromPages(this.#fsPath)
130
+ } catch (e) {
131
+ this.#error(
132
+ `url \`${this.#url}\` html file \`${join(this.#c.dirs.pages, this.#fsPath)}\` does not exist`,
133
+ )
134
+ return
135
+ }
131
136
  const document = parse(html)
132
137
  const imports: CollectedImports = {
133
138
  partials: [],
134
139
  scripts: [],
135
140
  }
141
+ let partials: Array<PartialContent>
136
142
  this.#collectImports(document, imports)
137
- const partials = await this.#resolvePartialContent(imports.partials)
143
+ const partialResults = await this.#resolvePartialContent(
144
+ imports.partials,
145
+ )
146
+ partials = partialResults.filter(p => p !== false)
147
+ // `dank serve` will not throw an error during a partial error, so this emits error and breaks out of `change` event handler
148
+ if (partials.length !== partialResults.length) {
149
+ this.#error(
150
+ `update to \`${join(this.#c.dirs.pages, this.#fsPath)}\` did not update to \`${this.#url}\` because of unresolved partials`,
151
+ )
152
+ return
153
+ }
154
+ if (this.#clientJS !== null) {
155
+ const decoration = await this.#clientJS.retrieve(
156
+ this.#c.esbuildPort,
157
+ )
158
+ this.#addScriptDecoration(document, decoration.js)
159
+ }
138
160
  if (update !== this.#update) {
139
161
  // another update has started so aborting this one
162
+ // only do synchronous work after this check
140
163
  return
141
164
  }
142
- this.#addDecorations(document)
143
- this.#update = update
144
165
  this.#document = document
145
166
  this.#partials = partials
146
167
  this.#scripts = imports.scripts
@@ -148,30 +169,93 @@ export class HtmlEntrypoint extends EventEmitter<HtmlEntrypointEvents> {
148
169
  imports,
149
170
  ...partials.map(p => p.imports),
150
171
  )
151
- // this.#entrypoints = new Set(entrypoints.map(entrypoint => entrypoint.in))
152
- this.emit('entrypoints', entrypoints)
153
- this.emit(
154
- 'partials',
155
- this.#partials.map(p => p.fsPath),
156
- )
172
+ LOG({
173
+ realm: 'html',
174
+ message: 'processed html entrypoint',
175
+ data: {
176
+ url: this.#url,
177
+ html: this.#fsPath,
178
+ entrypoints: entrypoints.map(ep => ep.in),
179
+ partials: partials.map(p => p.fsPath),
180
+ },
181
+ })
182
+ if (this.#haveEntrypointsChanged(entrypoints)) {
183
+ this.emit('entrypoints', entrypoints)
184
+ }
157
185
  if (this.listenerCount('output')) {
158
186
  this.emit('output', this.output())
159
187
  }
160
188
  }
161
189
 
162
- // Emits `partial` on detecting a partial reference for `dank serve` file watches
163
- // to respond to dependent changes
164
- // todo safeguard recursive partials that cause circular imports
190
+ #error(e: DankError | Error | string) {
191
+ let message: string
192
+ let error: Error
193
+ if (typeof e === 'string') {
194
+ message = e
195
+ error = new DankError(e)
196
+ } else {
197
+ message = e.message
198
+ error = e
199
+ }
200
+ LOG({
201
+ realm: 'html',
202
+ message: 'error processing html',
203
+ data: {
204
+ url: this.#url,
205
+ html: this.#fsPath,
206
+ error: message,
207
+ },
208
+ })
209
+ if (this.listenerCount('error')) {
210
+ this.emit('error', error)
211
+ } else {
212
+ throw error
213
+ }
214
+ }
215
+
216
+ #haveEntrypointsChanged(entrypoints: Array<EntryPoint>) {
217
+ const set = new Set(entrypoints.map(entrypoint => entrypoint.in))
218
+ const changed = set.symmetricDifference(this.#entrypoints).size > 0
219
+ this.#entrypoints = set
220
+ return changed
221
+ }
222
+
223
+ async #readFromPages(p: string): Promise<string> {
224
+ return await readFile(this.#resolver.absPagesPath(p), 'utf8')
225
+ }
226
+
165
227
  async #resolvePartialContent(
166
228
  partials: Array<PartialReference>,
167
- ): Promise<Array<PartialContent>> {
229
+ ): Promise<Array<PartialContent | false>> {
168
230
  return await Promise.all(
169
231
  partials.map(async p => {
170
- this.emit('partial', p.fsPath)
171
- const html = await readFile(
172
- join(this.#build.dirs.pages, p.fsPath),
173
- 'utf8',
174
- )
232
+ let html: string
233
+ if (isAbsolute(p.specifier)) {
234
+ this.#error(
235
+ `partials cannot be referenced with an absolute path like \`${p.specifier}\` in webpage \`${join(this.#c.dirs.pages, this.#fsPath)}\``,
236
+ )
237
+ return false
238
+ }
239
+ if (!this.#resolver.isPagesSubpathInPagesDir(p.fsPath)) {
240
+ this.#error(
241
+ `partials cannot be referenced from outside the pages dir like \`${p.specifier}\` in webpage \`${join(this.#c.dirs.pages, this.#fsPath)}\``,
242
+ )
243
+ return false
244
+ }
245
+ if (extname(p.fsPath) !== '.html') {
246
+ this.#error(
247
+ `partial path \`${p.fsPath}\` referenced by \`${this.#fsPath}\` is not a valid partial path`,
248
+ )
249
+ return false
250
+ }
251
+ try {
252
+ html = await this.#readFromPages(p.fsPath)
253
+ } catch (e) {
254
+ this.#error(
255
+ `partial \`${join('pages', p.fsPath)}\` imported by \`${join('pages', this.#fsPath)}\` does not exist`,
256
+ )
257
+ return false
258
+ }
175
259
  const fragment = parseFragment(html)
176
260
  const imports: CollectedImports = {
177
261
  partials: [],
@@ -181,10 +265,8 @@ export class HtmlEntrypoint extends EventEmitter<HtmlEntrypointEvents> {
181
265
  this.#rewritePartialRelativePaths(node, p.fsPath)
182
266
  })
183
267
  if (imports.partials.length) {
184
- // todo recursive partials?
185
- // await this.#resolvePartialContent(imports.partials)
186
- errorExit(
187
- `partial ${p.fsPath} cannot recursively import partials`,
268
+ this.#error(
269
+ `partials cannot import another partial like \`${join(this.#c.dirs.pages, p.fsPath)}\``,
188
270
  )
189
271
  }
190
272
  const content: PartialContent = {
@@ -219,35 +301,16 @@ export class HtmlEntrypoint extends EventEmitter<HtmlEntrypointEvents> {
219
301
  }
220
302
  }
221
303
 
222
- #addDecorations(document: Document) {
223
- if (!this.#decorations?.length) {
224
- return
225
- }
226
- for (const decoration of this.#decorations) {
227
- switch (decoration.type) {
228
- case 'script':
229
- const scriptNode = parseFragment(
230
- `<script type="module">${decoration.js}</script>`,
231
- ).childNodes[0]
232
- const htmlNode = document.childNodes.find(
233
- node => node.nodeName === 'html',
234
- ) as ParentNode
235
- const headNode = htmlNode.childNodes.find(
236
- node => node.nodeName === 'head',
237
- ) as ParentNode | undefined
238
- defaultTreeAdapter.appendChild(
239
- headNode || htmlNode,
240
- scriptNode,
241
- )
242
- break
243
- }
244
- }
245
- }
246
-
247
- output(hrefs?: HtmlHrefs): string {
248
- this.#injectPartials()
249
- this.#rewriteHrefs(hrefs)
250
- return serialize(this.#document)
304
+ #addScriptDecoration(document: Document, js: string) {
305
+ const scriptNode = parseFragment(`<script type="module">${js}</script>`)
306
+ .childNodes[0]
307
+ const htmlNode = document.childNodes.find(
308
+ node => node.nodeName === 'html',
309
+ ) as ParentNode
310
+ const headNode = htmlNode.childNodes.find(
311
+ node => node.nodeName === 'head',
312
+ ) as ParentNode | undefined
313
+ defaultTreeAdapter.appendChild(headNode || htmlNode, scriptNode)
251
314
  }
252
315
 
253
316
  // rewrites hrefs to content hashed urls
@@ -261,7 +324,7 @@ export class HtmlEntrypoint extends EventEmitter<HtmlEntrypointEvents> {
261
324
 
262
325
  async #injectPartials() {
263
326
  for (const { commentNode, fragment } of this.#partials) {
264
- if (!this.#build.production) {
327
+ if (!this.#c.flags.production) {
265
328
  defaultTreeAdapter.insertBefore(
266
329
  commentNode.parentNode!,
267
330
  defaultTreeAdapter.createCommentNode(commentNode.data),
@@ -275,7 +338,7 @@ export class HtmlEntrypoint extends EventEmitter<HtmlEntrypointEvents> {
275
338
  commentNode,
276
339
  )
277
340
  }
278
- if (this.#build.production) {
341
+ if (this.#c.flags.production) {
279
342
  defaultTreeAdapter.detachNode(commentNode)
280
343
  }
281
344
  }
@@ -293,24 +356,11 @@ export class HtmlEntrypoint extends EventEmitter<HtmlEntrypointEvents> {
293
356
  if (childNode.nodeName === '#comment' && 'data' in childNode) {
294
357
  const partialMatch = childNode.data.match(/\{\{(?<pp>.+)\}\}/)
295
358
  if (partialMatch) {
296
- const partialSpecifier = partialMatch.groups!.pp.trim()
297
- if (partialSpecifier.startsWith('/')) {
298
- errorExit(
299
- `partial ${partialSpecifier} in webpage ${this.#fsPath} cannot be an absolute path`,
300
- )
301
- }
302
- const partialPath = join(
303
- dirname(this.#fsPath),
304
- partialSpecifier,
305
- )
306
- if (!this.#resolver.isPagesSubpathInPagesDir(partialPath)) {
307
- errorExit(
308
- `partial ${partialSpecifier} in webpage ${this.#fsPath} cannot be outside of the pages directory`,
309
- )
310
- }
359
+ const specifier = partialMatch.groups!.pp.trim()
311
360
  collection.partials.push({
312
- fsPath: partialPath,
313
361
  commentNode: childNode,
362
+ fsPath: join(dirname(this.#fsPath), specifier),
363
+ specifier: specifier,
314
364
  })
315
365
  }
316
366
  } else if (childNode.nodeName === 'script') {
@@ -343,10 +393,10 @@ export class HtmlEntrypoint extends EventEmitter<HtmlEntrypointEvents> {
343
393
  href: string,
344
394
  elem: Element,
345
395
  ): ImportedScript {
346
- const inPath = join(this.#build.dirs.pages, dirname(this.#fsPath), href)
396
+ const inPath = join(this.#c.dirs.pages, dirname(this.#fsPath), href)
347
397
  if (!this.#resolver.isProjectSubpathInPagesDir(inPath)) {
348
- errorExit(
349
- `href ${href} in webpage ${this.#fsPath} cannot reference sources outside of the pages directory`,
398
+ throw new DankError(
399
+ `href \`${href}\` in webpage \`${join(this.#c.dirs.pages, this.#fsPath)}\` cannot reference sources outside of the pages directory`,
350
400
  )
351
401
  }
352
402
  let outPath = join(dirname(this.#fsPath), href)
@@ -406,8 +456,42 @@ function rewriteHrefs(scripts: Array<ImportedScript>, hrefs?: HtmlHrefs) {
406
456
  }
407
457
  }
408
458
 
409
- // todo evented error handling so HtmlEntrypoint can be unit tested
410
- function errorExit(msg: string): never {
411
- console.log(`\u001b[31merror:\u001b[0m`, msg)
412
- process.exit(1)
459
+ class ClientJS {
460
+ static #instance: ClientJS | null = null
461
+
462
+ static initialize(c: ResolvedDankConfig): ClientJS | null {
463
+ if (c.mode === 'build' || c.flags.preview) {
464
+ return null
465
+ } else if (!ClientJS.#instance) {
466
+ ClientJS.#instance = new ClientJS(c.esbuildPort)
467
+ }
468
+ return ClientJS.#instance
469
+ }
470
+
471
+ #esbuildPort: number
472
+ #read: Promise<string>
473
+ #result: Promise<HtmlDecoration>
474
+
475
+ private constructor(esbuildPort: number) {
476
+ this.#esbuildPort = esbuildPort
477
+ this.#read = readFile(
478
+ resolve(import.meta.dirname, join('..', 'client', 'client.js')),
479
+ 'utf-8',
480
+ )
481
+ this.#result = this.#read.then(this.#transform)
482
+ }
483
+
484
+ async retrieve(esbuildPort: number): Promise<HtmlDecoration> {
485
+ if (esbuildPort !== this.#esbuildPort) {
486
+ this.#result = this.#read.then(this.#transform)
487
+ }
488
+ return await this.#result
489
+ }
490
+
491
+ #transform = (js: string): HtmlDecoration => {
492
+ return {
493
+ type: 'script',
494
+ js: js.replace('3995', '' + this.#esbuildPort),
495
+ }
496
+ }
413
497
  }
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 { DankServe } from './flags.ts'
14
- import type { WebsiteManifest } from './metadata.ts'
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
- serve: DankServe,
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:' + serve.dankPort
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
- pageRoutes,
56
- serve,
51
+ flags,
52
+ dirs,
53
+ urlRewriteProvider,
57
54
  res,
58
55
  ),
59
56
  )
60
57
  }
61
58
  }
62
- createServer(serve.logHttp ? createLogWrapper(handler) : handler).listen(
63
- serve.dankPort,
59
+ createServer(flags.logHttp ? createLogWrapper(handler) : handler).listen(
60
+ port,
64
61
  )
65
62
  console.log(
66
- serve.preview ? 'preview' : 'dev',
67
- `server is live at http://127.0.0.1:${serve.dankPort}`,
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
- pageRoutes: PageRouteState,
77
- serve: DankServe,
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(url, pageRoutes, serve)
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 = pageRoutes.urlRewrites.find(urlRewrite =>
118
+ const urlRewrite = urlRewrites.find(urlRewrite =>
115
119
  urlRewrite.pattern.test(url.pathname),
116
120
  )
117
121
  return urlRewrite
118
- ? join(serve.dirs.buildWatch, urlRewrite.url, 'index.html')
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
- dir: string,
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(join(dir, url.pathname, 'index.html'), res)
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(join(dir, url.pathname), res)
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
- pageRoutes: PageRouteState,
203
- serve: DankServe,
220
+ esbuildPort: number,
221
+ dirs: DankDirectories,
222
+ registry: WebsiteRegistry,
204
223
  ): FrontendFetcher {
205
- const proxyAddress = 'http://127.0.0.1:' + serve.esbuildPort
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 (pageRoutes.urls.includes(url.pathname)) {
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(serve.dirs.public, url.pathname)
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 { DankBuild } from './flags.ts'
4
+ import type { DankDirectories } from './dirs.ts'
5
5
 
6
6
  export async function copyAssets(
7
- build: DankBuild,
7
+ dirs: DankDirectories,
8
8
  ): Promise<Array<string> | null> {
9
9
  try {
10
- const stats = await stat(build.dirs.public)
10
+ const stats = await stat(dirs.public)
11
11
  if (stats.isDirectory()) {
12
- await mkdir(build.dirs.buildDist, { recursive: true })
13
- return await recursiveCopyAssets(build)
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
- build: DankBuild,
25
+ dirs: DankDirectories,
26
26
  dir: string = '',
27
27
  ): Promise<Array<string>> {
28
28
  const copied: Array<string> = []
29
- const to = join(build.dirs.buildDist, dir)
29
+ const to = join(dirs.buildDist, dir)
30
30
  let madeDir = dir === ''
31
- const listingDir = join(build.dirs.public, dir)
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(build, join(dir, p))))
39
+ copied.push(...(await recursiveCopyAssets(dirs, join(dir, p))))
40
40
  } else {
41
41
  if (!madeDir) {
42
- await mkdir(join(build.dirs.buildDist, dir), {
42
+ await mkdir(join(dirs.buildDist, dir), {
43
43
  recursive: true,
44
44
  })
45
45
  madeDir = true