@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/serve.ts
CHANGED
|
@@ -1,58 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
readFile,
|
|
4
|
-
rm,
|
|
5
|
-
watch as _watch,
|
|
6
|
-
writeFile,
|
|
7
|
-
} from 'node:fs/promises'
|
|
8
|
-
import { extname, join, resolve } from 'node:path'
|
|
1
|
+
import { mkdir, rm, writeFile } from 'node:fs/promises'
|
|
2
|
+
import { extname, join } from 'node:path'
|
|
9
3
|
import type { BuildContext } from 'esbuild'
|
|
10
4
|
import { buildWebsite } from './build.ts'
|
|
11
|
-
import { loadConfig } from './config.ts'
|
|
12
|
-
import type { DankConfig } from './dank.ts'
|
|
5
|
+
import { loadConfig, type ResolvedDankConfig } from './config.ts'
|
|
13
6
|
import { createGlobalDefinitions } from './define.ts'
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { HtmlEntrypoint } from './html.ts'
|
|
7
|
+
import { LOG } from './developer.ts'
|
|
8
|
+
import { esbuildDevContext } from './esbuild.ts'
|
|
9
|
+
import type { HtmlEntrypoint } from './html.ts'
|
|
17
10
|
import {
|
|
18
11
|
createBuiltDistFilesFetcher,
|
|
19
12
|
createDevServeFilesFetcher,
|
|
20
13
|
startWebServer,
|
|
21
|
-
type PageRouteState,
|
|
22
|
-
type UrlRewrite,
|
|
23
14
|
} from './http.ts'
|
|
24
|
-
import { WebsiteRegistry } from './
|
|
15
|
+
import { WebsiteRegistry, type UrlRewrite } from './registry.ts'
|
|
25
16
|
import { startDevServices, updateDevServices } from './services.ts'
|
|
17
|
+
import { watch } from './watch.ts'
|
|
18
|
+
|
|
19
|
+
let c: ResolvedDankConfig
|
|
26
20
|
|
|
27
|
-
export async function serveWebsite(
|
|
28
|
-
|
|
29
|
-
await rm(
|
|
21
|
+
export async function serveWebsite(): Promise<never> {
|
|
22
|
+
c = await loadConfig('serve', process.cwd())
|
|
23
|
+
await rm(c.dirs.buildRoot, { force: true, recursive: true })
|
|
30
24
|
const abortController = new AbortController()
|
|
31
25
|
process.once('exit', () => abortController.abort())
|
|
32
|
-
if (
|
|
33
|
-
await startPreviewMode(
|
|
26
|
+
if (c.flags.preview) {
|
|
27
|
+
await startPreviewMode(abortController.signal)
|
|
34
28
|
} else {
|
|
35
|
-
await startDevMode(
|
|
29
|
+
await startDevMode(abortController.signal)
|
|
36
30
|
}
|
|
37
31
|
return new Promise(() => {})
|
|
38
32
|
}
|
|
39
33
|
|
|
40
|
-
async function startPreviewMode(
|
|
41
|
-
c
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
const manifest = await buildWebsite(c, serve)
|
|
46
|
-
const frontend = createBuiltDistFilesFetcher(serve.dirs.buildDist, manifest)
|
|
47
|
-
const devServices = startDevServices(c, signal)
|
|
48
|
-
startWebServer(serve, frontend, devServices.http, {
|
|
49
|
-
urls: Object.keys(c.pages),
|
|
50
|
-
urlRewrites: collectUrlRewrites(c),
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function collectUrlRewrites(c: DankConfig): Array<UrlRewrite> {
|
|
55
|
-
return Object.keys(c.pages)
|
|
34
|
+
async function startPreviewMode(signal: AbortSignal) {
|
|
35
|
+
const manifest = await buildWebsite(c)
|
|
36
|
+
const frontend = createBuiltDistFilesFetcher(c.dirs, manifest)
|
|
37
|
+
const devServices = startDevServices(c.services, signal)
|
|
38
|
+
const urlRewrites: Array<UrlRewrite> = Object.keys(c.pages)
|
|
56
39
|
.sort()
|
|
57
40
|
.map(url => {
|
|
58
41
|
const mapping = c.pages[url as `/${string}`]
|
|
@@ -61,6 +44,14 @@ function collectUrlRewrites(c: DankConfig): Array<UrlRewrite> {
|
|
|
61
44
|
: { url, pattern: mapping.pattern }
|
|
62
45
|
})
|
|
63
46
|
.filter(mapping => mapping !== null)
|
|
47
|
+
startWebServer(
|
|
48
|
+
c.dankPort,
|
|
49
|
+
c.flags,
|
|
50
|
+
c.dirs,
|
|
51
|
+
{ urlRewrites },
|
|
52
|
+
frontend,
|
|
53
|
+
devServices.http,
|
|
54
|
+
)
|
|
64
55
|
}
|
|
65
56
|
|
|
66
57
|
type BuildContextState =
|
|
@@ -70,152 +61,39 @@ type BuildContextState =
|
|
|
70
61
|
| 'disposing'
|
|
71
62
|
| null
|
|
72
63
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// todo changing partials triggers update on html pages
|
|
79
|
-
async function startDevMode(
|
|
80
|
-
c: DankConfig,
|
|
81
|
-
serve: DankServe,
|
|
82
|
-
signal: AbortSignal,
|
|
83
|
-
) {
|
|
84
|
-
await mkdir(serve.dirs.buildWatch, { recursive: true })
|
|
85
|
-
const registry = new WebsiteRegistry(serve)
|
|
86
|
-
const clientJS = await loadClientJS(serve.esbuildPort)
|
|
87
|
-
const pagesByUrlPath: Record<string, HtmlEntrypoint> = {}
|
|
88
|
-
const partialsByUrlPath: Record<string, Array<string>> = {}
|
|
89
|
-
const entryPointsByUrlPath: Record<string, EntrypointsState> = {}
|
|
64
|
+
async function startDevMode(signal: AbortSignal) {
|
|
65
|
+
const registry = new WebsiteRegistry(c)
|
|
66
|
+
await mkdir(c.dirs.buildWatch, { recursive: true })
|
|
90
67
|
let buildContext: BuildContextState = null
|
|
91
68
|
|
|
92
|
-
registry.on('workers', resetBuildContext)
|
|
93
|
-
|
|
94
69
|
watch('dank.config.ts', signal, async () => {
|
|
95
|
-
let updated: DankConfig
|
|
96
70
|
try {
|
|
97
|
-
|
|
71
|
+
await c.reload()
|
|
98
72
|
} catch (ignore) {
|
|
99
73
|
return
|
|
100
74
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
Object.entries(updated.pages).map(async ([urlPath, mapping]) => {
|
|
104
|
-
c.pages[urlPath as `/${string}`] = mapping
|
|
105
|
-
const srcPath =
|
|
106
|
-
typeof mapping === 'string' ? mapping : mapping.webpage
|
|
107
|
-
if (!pagesByUrlPath[urlPath]) {
|
|
108
|
-
await addPage(urlPath, srcPath)
|
|
109
|
-
} else {
|
|
110
|
-
prevPages.delete(urlPath)
|
|
111
|
-
if (pagesByUrlPath[urlPath].fsPath !== srcPath) {
|
|
112
|
-
await updatePage(urlPath)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}),
|
|
116
|
-
)
|
|
117
|
-
for (const prevPage of Array.from(prevPages)) {
|
|
118
|
-
delete c.pages[prevPage as `/${string}`]
|
|
119
|
-
deletePage(prevPage)
|
|
120
|
-
}
|
|
121
|
-
updateDevServices(updated)
|
|
75
|
+
registry.configSync()
|
|
76
|
+
updateDevServices(c.services)
|
|
122
77
|
})
|
|
123
78
|
|
|
124
|
-
watch(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
for (const [urlPath, partials] of Object.entries(
|
|
132
|
-
partialsByUrlPath,
|
|
133
|
-
)) {
|
|
134
|
-
if (partials.includes(filename)) {
|
|
135
|
-
updatePage(urlPath, filename)
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
await Promise.all(
|
|
142
|
-
Object.entries(c.pages).map(async ([urlPath, mapping]) => {
|
|
143
|
-
const srcPath =
|
|
144
|
-
typeof mapping === 'string' ? mapping : mapping.webpage
|
|
145
|
-
await addPage(urlPath, srcPath)
|
|
146
|
-
return new Promise(res =>
|
|
147
|
-
pagesByUrlPath[urlPath].once('entrypoints', res),
|
|
148
|
-
)
|
|
149
|
-
}),
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
async function addPage(urlPath: string, srcPath: string) {
|
|
153
|
-
await mkdir(join(serve.dirs.buildWatch, urlPath), { recursive: true })
|
|
154
|
-
const htmlEntrypoint = (pagesByUrlPath[urlPath] = new HtmlEntrypoint(
|
|
155
|
-
serve,
|
|
156
|
-
registry.resolver,
|
|
157
|
-
urlPath,
|
|
158
|
-
srcPath,
|
|
159
|
-
[{ type: 'script', js: clientJS }],
|
|
160
|
-
))
|
|
161
|
-
htmlEntrypoint.on('entrypoints', entrypoints => {
|
|
162
|
-
const pathsIn = new Set(entrypoints.map(e => e.in))
|
|
163
|
-
if (
|
|
164
|
-
!entryPointsByUrlPath[urlPath] ||
|
|
165
|
-
!matchingEntrypoints(
|
|
166
|
-
entryPointsByUrlPath[urlPath].pathsIn,
|
|
167
|
-
pathsIn,
|
|
168
|
-
)
|
|
169
|
-
) {
|
|
170
|
-
entryPointsByUrlPath[urlPath] = { entrypoints, pathsIn }
|
|
171
|
-
resetBuildContext()
|
|
172
|
-
}
|
|
173
|
-
})
|
|
174
|
-
htmlEntrypoint.on('partial', partial => {
|
|
175
|
-
if (!partialsByUrlPath[urlPath]) {
|
|
176
|
-
partialsByUrlPath[urlPath] = []
|
|
177
|
-
}
|
|
178
|
-
partialsByUrlPath[urlPath].push(partial)
|
|
79
|
+
watch(c.dirs.pages, signal, filename => {
|
|
80
|
+
LOG({
|
|
81
|
+
realm: 'serve',
|
|
82
|
+
message: 'pages dir watch event',
|
|
83
|
+
data: {
|
|
84
|
+
file: filename,
|
|
85
|
+
},
|
|
179
86
|
})
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function deletePage(urlPath: string) {
|
|
190
|
-
pagesByUrlPath[urlPath].removeAllListeners()
|
|
191
|
-
delete pagesByUrlPath[urlPath]
|
|
192
|
-
delete entryPointsByUrlPath[urlPath]
|
|
193
|
-
resetBuildContext()
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
async function updatePage(urlPath: string, partial?: string) {
|
|
197
|
-
pagesByUrlPath[urlPath].emit('change', partial)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function collectEntrypoints(): Array<EntryPoint> {
|
|
201
|
-
const unique: Set<string> = new Set()
|
|
202
|
-
const pageBundles = Object.values(entryPointsByUrlPath)
|
|
203
|
-
.flatMap(entrypointState => entrypointState.entrypoints)
|
|
204
|
-
.filter(entryPoint => {
|
|
205
|
-
if (unique.has(entryPoint.in)) {
|
|
206
|
-
return false
|
|
207
|
-
} else {
|
|
208
|
-
unique.add(entryPoint.in)
|
|
209
|
-
return true
|
|
87
|
+
if (extname(filename) === '.html') {
|
|
88
|
+
registry.htmlEntrypoints.forEach(html => {
|
|
89
|
+
if (html.fsPath === filename) {
|
|
90
|
+
html.emit('change')
|
|
91
|
+
} else if (html.usesPartial(filename)) {
|
|
92
|
+
html.emit('change', filename)
|
|
210
93
|
}
|
|
211
94
|
})
|
|
212
|
-
const workerBundles = registry.workerEntryPoints()
|
|
213
|
-
if (workerBundles) {
|
|
214
|
-
return [...pageBundles, ...workerBundles]
|
|
215
|
-
} else {
|
|
216
|
-
return pageBundles
|
|
217
95
|
}
|
|
218
|
-
}
|
|
96
|
+
})
|
|
219
97
|
|
|
220
98
|
function resetBuildContext() {
|
|
221
99
|
switch (buildContext) {
|
|
@@ -227,6 +105,7 @@ async function startDevMode(
|
|
|
227
105
|
return
|
|
228
106
|
}
|
|
229
107
|
if (buildContext !== null) {
|
|
108
|
+
LOG({ realm: 'serve', message: 'disposing esbuild context' })
|
|
230
109
|
const disposing = buildContext.dispose()
|
|
231
110
|
buildContext = 'disposing'
|
|
232
111
|
disposing.then(() => {
|
|
@@ -235,84 +114,84 @@ async function startDevMode(
|
|
|
235
114
|
})
|
|
236
115
|
} else {
|
|
237
116
|
buildContext = 'starting'
|
|
238
|
-
startEsbuildWatch(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
},
|
|
250
|
-
)
|
|
117
|
+
startEsbuildWatch(registry).then(ctx => {
|
|
118
|
+
if (buildContext === 'dirty') {
|
|
119
|
+
buildContext = 'disposing'
|
|
120
|
+
ctx.dispose().then(() => {
|
|
121
|
+
buildContext = null
|
|
122
|
+
resetBuildContext()
|
|
123
|
+
})
|
|
124
|
+
} else {
|
|
125
|
+
buildContext = ctx
|
|
126
|
+
}
|
|
127
|
+
})
|
|
251
128
|
}
|
|
252
129
|
}
|
|
253
130
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
131
|
+
registry.on('webpage', html => {
|
|
132
|
+
html.on('error', e =>
|
|
133
|
+
console.log(`\u001b[31merror:\u001b[0m`, e.message),
|
|
134
|
+
)
|
|
135
|
+
html.on('output', output => writeHtml(html, output))
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
registry.on('workers', () => {
|
|
139
|
+
LOG({
|
|
140
|
+
realm: 'serve',
|
|
141
|
+
message: 'registry updated worker entrypoints',
|
|
142
|
+
data: {
|
|
143
|
+
workers: registry.workerEntryPoints?.map(ep => ep.in) || null,
|
|
144
|
+
},
|
|
145
|
+
})
|
|
146
|
+
resetBuildContext()
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
registry.configSync()
|
|
150
|
+
await Promise.all(registry.htmlEntrypoints.map(html => html.process()))
|
|
151
|
+
|
|
152
|
+
// listen for entrypoint diffs after processing webpages
|
|
153
|
+
registry.on('entrypoints', () => resetBuildContext())
|
|
264
154
|
|
|
265
155
|
// inital start of esbuild ctx
|
|
266
156
|
resetBuildContext()
|
|
267
157
|
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const devServices = startDevServices(c, signal)
|
|
279
|
-
startWebServer(serve, frontend, devServices.http, pageRoutes)
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function matchingEntrypoints(a: Set<string>, b: Set<string>): boolean {
|
|
283
|
-
if (a.size !== b.size) {
|
|
284
|
-
return false
|
|
285
|
-
}
|
|
286
|
-
for (const v in a) {
|
|
287
|
-
if (!b.has(v)) {
|
|
288
|
-
return false
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
return true
|
|
158
|
+
const frontend = createDevServeFilesFetcher(c.esbuildPort, c.dirs, registry)
|
|
159
|
+
const devServices = startDevServices(c.services, signal)
|
|
160
|
+
startWebServer(
|
|
161
|
+
c.dankPort,
|
|
162
|
+
c.flags,
|
|
163
|
+
c.dirs,
|
|
164
|
+
registry,
|
|
165
|
+
frontend,
|
|
166
|
+
devServices.http,
|
|
167
|
+
)
|
|
292
168
|
}
|
|
293
169
|
|
|
294
170
|
async function startEsbuildWatch(
|
|
295
|
-
c: DankConfig,
|
|
296
171
|
registry: WebsiteRegistry,
|
|
297
|
-
serve: DankServe,
|
|
298
|
-
entryPoints: Array<EntryPoint>,
|
|
299
172
|
): Promise<BuildContext> {
|
|
173
|
+
const entryPoints = registry.webpageAndWorkerEntryPoints
|
|
174
|
+
LOG({
|
|
175
|
+
realm: 'serve',
|
|
176
|
+
message: 'starting esbuild watch',
|
|
177
|
+
data: {
|
|
178
|
+
entrypoints: entryPoints.map(ep => ep.in),
|
|
179
|
+
},
|
|
180
|
+
})
|
|
300
181
|
const ctx = await esbuildDevContext(
|
|
301
|
-
serve,
|
|
302
182
|
registry,
|
|
303
|
-
createGlobalDefinitions(
|
|
183
|
+
createGlobalDefinitions(c),
|
|
304
184
|
entryPoints,
|
|
305
|
-
c.esbuild,
|
|
306
185
|
)
|
|
307
186
|
|
|
308
187
|
await ctx.watch()
|
|
309
188
|
|
|
310
189
|
await ctx.serve({
|
|
311
190
|
host: '127.0.0.1',
|
|
312
|
-
port:
|
|
191
|
+
port: c.esbuildPort,
|
|
313
192
|
cors: {
|
|
314
193
|
origin: ['127.0.0.1', 'localhost'].map(
|
|
315
|
-
hostname => `http://${hostname}:${
|
|
194
|
+
hostname => `http://${hostname}:${c.dankPort}`,
|
|
316
195
|
),
|
|
317
196
|
},
|
|
318
197
|
})
|
|
@@ -320,48 +199,17 @@ async function startEsbuildWatch(
|
|
|
320
199
|
return ctx
|
|
321
200
|
}
|
|
322
201
|
|
|
323
|
-
async function
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
)
|
|
336
|
-
const delayFire = 90
|
|
337
|
-
const timeout = 100
|
|
338
|
-
let changes: Record<string, number> = {}
|
|
339
|
-
try {
|
|
340
|
-
for await (const { filename } of _watch(p, {
|
|
341
|
-
recursive: true,
|
|
342
|
-
signal,
|
|
343
|
-
})) {
|
|
344
|
-
if (filename) {
|
|
345
|
-
if (!changes[filename]) {
|
|
346
|
-
const now = Date.now()
|
|
347
|
-
changes[filename] = now + delayFire
|
|
348
|
-
setTimeout(() => {
|
|
349
|
-
const now = Date.now()
|
|
350
|
-
for (const [filename, then] of Object.entries(
|
|
351
|
-
changes,
|
|
352
|
-
)) {
|
|
353
|
-
if (then <= now) {
|
|
354
|
-
fire(filename)
|
|
355
|
-
delete changes[filename]
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}, timeout)
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
} catch (e: any) {
|
|
363
|
-
if (e.name !== 'AbortError') {
|
|
364
|
-
throw e
|
|
365
|
-
}
|
|
366
|
-
}
|
|
202
|
+
async function writeHtml(html: HtmlEntrypoint, output: string) {
|
|
203
|
+
const dir = join(c.dirs.buildWatch, html.url)
|
|
204
|
+
await mkdir(dir, { recursive: true })
|
|
205
|
+
const path = join(dir, 'index.html')
|
|
206
|
+
LOG({
|
|
207
|
+
realm: 'serve',
|
|
208
|
+
message: 'writing html output',
|
|
209
|
+
data: {
|
|
210
|
+
webpage: html.fsPath,
|
|
211
|
+
path,
|
|
212
|
+
},
|
|
213
|
+
})
|
|
214
|
+
await writeFile(path, output)
|
|
367
215
|
}
|
package/lib/services.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ChildProcess, spawn } from 'node:child_process'
|
|
2
2
|
import { basename, isAbsolute, resolve } from 'node:path'
|
|
3
|
-
import type {
|
|
3
|
+
import type { DevService, ResolvedDankConfig } from './config.ts'
|
|
4
4
|
|
|
5
5
|
export type DevServices = {
|
|
6
6
|
http: HttpServices
|
|
@@ -24,26 +24,26 @@ let updating: null | {
|
|
|
24
24
|
} = null
|
|
25
25
|
|
|
26
26
|
export function startDevServices(
|
|
27
|
-
|
|
27
|
+
services: ResolvedDankConfig['services'],
|
|
28
28
|
_signal: AbortSignal,
|
|
29
29
|
): DevServices {
|
|
30
30
|
signal = _signal
|
|
31
|
-
if (
|
|
32
|
-
for (const s of
|
|
31
|
+
if (services?.length) {
|
|
32
|
+
for (const s of services) {
|
|
33
33
|
running.push({ s, process: startService(s) })
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
return {
|
|
37
37
|
http: {
|
|
38
|
-
get running(): Array<
|
|
38
|
+
get running(): Array<HttpService> {
|
|
39
39
|
return running.map(({ s }) => s.http).filter(http => !!http)
|
|
40
40
|
},
|
|
41
41
|
},
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export function updateDevServices(
|
|
46
|
-
if (!
|
|
45
|
+
export function updateDevServices(services: ResolvedDankConfig['services']) {
|
|
46
|
+
if (!services?.length) {
|
|
47
47
|
if (running.length) {
|
|
48
48
|
if (updating === null) {
|
|
49
49
|
updating = { stopping: [], starting: [] }
|
|
@@ -63,7 +63,7 @@ export function updateDevServices(c: DankConfig) {
|
|
|
63
63
|
}
|
|
64
64
|
const keep = []
|
|
65
65
|
const next: Array<DevService> = []
|
|
66
|
-
for (const s of
|
|
66
|
+
for (const s of services) {
|
|
67
67
|
let found = false
|
|
68
68
|
for (let i = 0; i < running.length; i++) {
|
|
69
69
|
const p = running[i].s
|
package/lib/watch.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { watch as createWatch } from 'node:fs/promises'
|
|
2
|
+
|
|
3
|
+
export async function watch(
|
|
4
|
+
p: string,
|
|
5
|
+
signal: AbortSignal,
|
|
6
|
+
fire: (filename: string) => void,
|
|
7
|
+
) {
|
|
8
|
+
const delayFire = 90
|
|
9
|
+
const timeout = 100
|
|
10
|
+
let changes: Record<string, number> = {}
|
|
11
|
+
try {
|
|
12
|
+
for await (const { filename } of createWatch(p, {
|
|
13
|
+
recursive: true,
|
|
14
|
+
signal,
|
|
15
|
+
})) {
|
|
16
|
+
if (filename) {
|
|
17
|
+
if (!changes[filename]) {
|
|
18
|
+
const now = Date.now()
|
|
19
|
+
changes[filename] = now + delayFire
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
const now = Date.now()
|
|
22
|
+
for (const [filename, then] of Object.entries(
|
|
23
|
+
changes,
|
|
24
|
+
)) {
|
|
25
|
+
if (then <= now) {
|
|
26
|
+
fire(filename)
|
|
27
|
+
delete changes[filename]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}, timeout)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} catch (e: any) {
|
|
35
|
+
if (e.name !== 'AbortError') {
|
|
36
|
+
throw e
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|