@eighty4/dank 0.0.5-1 → 0.0.5-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/build.ts +1 -2
- package/lib/build_tag.ts +119 -15
- package/lib/config.ts +28 -0
- package/lib/dank.ts +10 -2
- package/lib/dirs.ts +8 -9
- package/lib/http.ts +9 -10
- package/lib/serve.ts +65 -21
- package/lib/services.ts +242 -204
- package/lib/watch.ts +38 -7
- package/lib_js/build.js +1 -2
- package/lib_js/build_tag.js +74 -14
- package/lib_js/config.js +20 -0
- package/lib_js/dirs.js +6 -6
- package/lib_js/http.js +8 -9
- package/lib_js/serve.js +45 -14
- package/lib_js/services.js +158 -169
- package/lib_js/watch.js +14 -5
- package/lib_types/dank.d.ts +5 -0
- package/package.json +1 -1
package/lib/build.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
|
-
import { createBuildTag } from './build_tag.ts'
|
|
4
3
|
import { loadConfig, type ResolvedDankConfig } from './config.ts'
|
|
5
4
|
import { type DefineDankGlobal, createGlobalDefinitions } from './define.ts'
|
|
6
5
|
import type { DankDirectories } from './dirs.ts'
|
|
@@ -14,7 +13,7 @@ export async function buildWebsite(
|
|
|
14
13
|
if (!c) {
|
|
15
14
|
c = await loadConfig('build', process.cwd())
|
|
16
15
|
}
|
|
17
|
-
const buildTag = await
|
|
16
|
+
const buildTag = await c.buildTag()
|
|
18
17
|
console.log(
|
|
19
18
|
c.flags.minify
|
|
20
19
|
? c.flags.production
|
package/lib/build_tag.ts
CHANGED
|
@@ -1,25 +1,129 @@
|
|
|
1
1
|
import { exec } from 'node:child_process'
|
|
2
|
+
import type { DankConfig } from './dank.ts'
|
|
2
3
|
import type { DankFlags } from './flags.ts'
|
|
3
4
|
|
|
4
|
-
export async function createBuildTag(
|
|
5
|
+
export async function createBuildTag(
|
|
6
|
+
projectDir: string,
|
|
7
|
+
flags: DankFlags,
|
|
8
|
+
buildTagSource?: DankConfig['buildTag'],
|
|
9
|
+
): Promise<string> {
|
|
10
|
+
if (typeof buildTagSource === 'function') {
|
|
11
|
+
buildTagSource = await buildTagSource({ production: flags.production })
|
|
12
|
+
}
|
|
13
|
+
if (typeof buildTagSource === 'undefined' || buildTagSource === null) {
|
|
14
|
+
buildTagSource = await resolveExpressionDefault(projectDir)
|
|
15
|
+
}
|
|
16
|
+
if (typeof buildTagSource !== 'string') {
|
|
17
|
+
throw TypeError(
|
|
18
|
+
'DankConfig.buildTag must resolve to a string expession',
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
const params: BuildTagParams = {}
|
|
5
22
|
const now = new Date()
|
|
23
|
+
const paramPattern = new RegExp(/{{\s*(?<name>[a-z][A-Za-z]+)\s*}}/g)
|
|
24
|
+
let paramMatch: RegExpExecArray | null
|
|
25
|
+
let buildTag = buildTagSource
|
|
26
|
+
let offset = 0
|
|
27
|
+
while ((paramMatch = paramPattern.exec(buildTagSource)) != null) {
|
|
28
|
+
const paramName = paramMatch.groups!.name.trim() as keyof BuildTagParams
|
|
29
|
+
let paramValue: string
|
|
30
|
+
if (params[paramName]) {
|
|
31
|
+
paramValue = params[paramName]
|
|
32
|
+
} else {
|
|
33
|
+
paramValue = params[paramName] = await getParamValue(
|
|
34
|
+
projectDir,
|
|
35
|
+
paramName,
|
|
36
|
+
now,
|
|
37
|
+
buildTagSource,
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
buildTag =
|
|
41
|
+
buildTag.substring(0, paramMatch.index + offset) +
|
|
42
|
+
paramValue +
|
|
43
|
+
buildTag.substring(paramMatch.index + paramMatch[0].length + offset)
|
|
44
|
+
offset += paramValue.length - paramMatch[0].length
|
|
45
|
+
}
|
|
46
|
+
const validate = /^[A-Za-z\d][A-Za-z\d-_\.]+$/
|
|
47
|
+
if (!validate.test(buildTag)) {
|
|
48
|
+
throw Error(
|
|
49
|
+
`build tag ${buildTag} does not pass pattern ${validate.source} validation`,
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
return buildTag
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function resolveExpressionDefault(projectDir: string): Promise<string> {
|
|
56
|
+
const base = '{{ date }}-{{ timeMS }}'
|
|
57
|
+
const isGitRepo = await new Promise(res =>
|
|
58
|
+
exec('git rev-parse --is-inside-work-tree', { cwd: projectDir }, err =>
|
|
59
|
+
res(!err),
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
return isGitRepo ? base + '-{{ gitHash }}' : base
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
type BuildTagParams = {
|
|
66
|
+
date?: string
|
|
67
|
+
gitHash?: string
|
|
68
|
+
timeMS?: string
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function getParamValue(
|
|
72
|
+
projectDir: string,
|
|
73
|
+
name: keyof BuildTagParams,
|
|
74
|
+
now: Date,
|
|
75
|
+
buildTagSource: string,
|
|
76
|
+
): Promise<string> {
|
|
77
|
+
switch (name) {
|
|
78
|
+
case 'date':
|
|
79
|
+
return getDate(now)
|
|
80
|
+
case 'gitHash':
|
|
81
|
+
try {
|
|
82
|
+
return await getGitHash(projectDir)
|
|
83
|
+
} catch (e) {
|
|
84
|
+
if (e === 'not-repo') {
|
|
85
|
+
throw Error(
|
|
86
|
+
`buildTag cannot use \`gitHash\` in \`${buildTagSource}\` outside of a git repository`,
|
|
87
|
+
)
|
|
88
|
+
} else {
|
|
89
|
+
throw e
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
case 'timeMS':
|
|
93
|
+
return getTimeMS(now)
|
|
94
|
+
default:
|
|
95
|
+
throw Error(name + ' is not a supported build tag param')
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function getDate(now: Date): string {
|
|
100
|
+
return now.toISOString().substring(0, 10)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function getGitHash(projectDir: string): Promise<string> {
|
|
104
|
+
return await new Promise((res, rej) =>
|
|
105
|
+
exec(
|
|
106
|
+
'git rev-parse --short HEAD',
|
|
107
|
+
{ cwd: projectDir },
|
|
108
|
+
(err, stdout, stderr) => {
|
|
109
|
+
if (err) {
|
|
110
|
+
if (stderr.includes('not a git repository')) {
|
|
111
|
+
rej('not-repo')
|
|
112
|
+
} else {
|
|
113
|
+
rej(err)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
res(stdout.trim())
|
|
117
|
+
},
|
|
118
|
+
),
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getTimeMS(now: Date): string {
|
|
6
123
|
const ms =
|
|
7
124
|
now.getUTCMilliseconds() +
|
|
8
125
|
now.getUTCSeconds() * 1000 +
|
|
9
126
|
now.getUTCMinutes() * 1000 * 60 +
|
|
10
127
|
now.getUTCHours() * 1000 * 60 * 60
|
|
11
|
-
|
|
12
|
-
const time = String(ms).padStart(8, '0')
|
|
13
|
-
const when = `${date}-${time}`
|
|
14
|
-
if (flags.production) {
|
|
15
|
-
const gitHash = await new Promise((res, rej) =>
|
|
16
|
-
exec('git rev-parse --short HEAD', (err, stdout) => {
|
|
17
|
-
if (err) rej(err)
|
|
18
|
-
res(stdout.trim())
|
|
19
|
-
}),
|
|
20
|
-
)
|
|
21
|
-
return `${when}-${gitHash}`
|
|
22
|
-
} else {
|
|
23
|
-
return when
|
|
24
|
-
}
|
|
128
|
+
return String(ms).padStart(8, '0')
|
|
25
129
|
}
|
package/lib/config.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isAbsolute, resolve } from 'node:path'
|
|
2
|
+
import { createBuildTag } from './build_tag.ts'
|
|
2
3
|
import type {
|
|
3
4
|
DankConfig,
|
|
4
5
|
DankDetails,
|
|
@@ -34,6 +35,8 @@ export type ResolvedDankConfig = {
|
|
|
34
35
|
get devPages(): Readonly<DankConfig['devPages']>
|
|
35
36
|
get services(): Readonly<DankConfig['services']>
|
|
36
37
|
|
|
38
|
+
buildTag(): Promise<string>
|
|
39
|
+
|
|
37
40
|
reload(): Promise<void>
|
|
38
41
|
}
|
|
39
42
|
|
|
@@ -60,6 +63,7 @@ export async function loadConfig(
|
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
class DankConfigInternal implements ResolvedDankConfig {
|
|
66
|
+
#buildTag: DankConfig['buildTag']
|
|
63
67
|
#dirs: Readonly<DankDirectories>
|
|
64
68
|
#flags: Readonly<DankFlags>
|
|
65
69
|
#mode: 'build' | 'serve'
|
|
@@ -119,11 +123,20 @@ class DankConfigInternal implements ResolvedDankConfig {
|
|
|
119
123
|
return this.#services
|
|
120
124
|
}
|
|
121
125
|
|
|
126
|
+
async buildTag(): Promise<string> {
|
|
127
|
+
return await createBuildTag(
|
|
128
|
+
this.#dirs.projectRootAbs,
|
|
129
|
+
this.#flags,
|
|
130
|
+
this.#buildTag,
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
122
134
|
async reload() {
|
|
123
135
|
const userConfig = await resolveConfig(
|
|
124
136
|
this.#modulePath,
|
|
125
137
|
resolveDankDetails(this.#mode, this.#flags),
|
|
126
138
|
)
|
|
139
|
+
this.#buildTag = userConfig.buildTag
|
|
127
140
|
this.#dankPort = resolveDankPort(this.#flags, userConfig)
|
|
128
141
|
this.#esbuildPort = resolveEsbuildPort(this.#flags, userConfig)
|
|
129
142
|
this.#esbuild = Object.freeze(userConfig.esbuild)
|
|
@@ -173,6 +186,7 @@ function resolveDankDetails(
|
|
|
173
186
|
function validateDankConfig(c: Partial<DankConfig>) {
|
|
174
187
|
try {
|
|
175
188
|
validatePorts(c)
|
|
189
|
+
validateBuildTag(c.buildTag)
|
|
176
190
|
validatePages(c.pages)
|
|
177
191
|
validateDevPages(c.devPages)
|
|
178
192
|
validateDevServices(c.services)
|
|
@@ -202,6 +216,20 @@ function validatePorts(c: Partial<DankConfig>) {
|
|
|
202
216
|
}
|
|
203
217
|
}
|
|
204
218
|
|
|
219
|
+
function validateBuildTag(buildTag: DankConfig['buildTag']) {
|
|
220
|
+
if (buildTag === null) {
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
switch (typeof buildTag) {
|
|
224
|
+
case 'undefined':
|
|
225
|
+
case 'string':
|
|
226
|
+
case 'function':
|
|
227
|
+
return
|
|
228
|
+
default:
|
|
229
|
+
throw Error('DankConfig.buildTag must be a string or function')
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
205
233
|
function validateEsbuildConfig(esbuild?: EsbuildConfig) {
|
|
206
234
|
if (esbuild?.loaders !== null && typeof esbuild?.loaders !== 'undefined') {
|
|
207
235
|
if (typeof esbuild.loaders !== 'object') {
|
package/lib/dank.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Plugin as EsbuildPlugin } from 'esbuild'
|
|
2
2
|
|
|
3
3
|
export type DankConfig = {
|
|
4
|
-
// used for
|
|
5
|
-
|
|
4
|
+
// used for service worker caching
|
|
5
|
+
buildTag?: string | BuildTagBuilder
|
|
6
6
|
|
|
7
7
|
// customize esbuild configs
|
|
8
8
|
esbuild?: EsbuildConfig
|
|
@@ -26,6 +26,14 @@ export type DankConfig = {
|
|
|
26
26
|
services?: Array<DevService>
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
export type BuildTagParams = {
|
|
30
|
+
production: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type BuildTagBuilder = (
|
|
34
|
+
build: BuildTagParams,
|
|
35
|
+
) => Promise<string> | string
|
|
36
|
+
|
|
29
37
|
// extend an html entrypoint with url rewriting similar to cdn configurations
|
|
30
38
|
// after trying all webpage, bundle and asset paths, mapping patterns
|
|
31
39
|
// will be tested in the alphabetical order of the webpage paths
|
package/lib/dirs.ts
CHANGED
|
@@ -7,8 +7,7 @@ export type DankDirectories = {
|
|
|
7
7
|
buildWatch: string
|
|
8
8
|
buildDist: string
|
|
9
9
|
pages: string
|
|
10
|
-
|
|
11
|
-
projectResolved: string
|
|
10
|
+
pagesAbs: string
|
|
12
11
|
projectRootAbs: string
|
|
13
12
|
public: string
|
|
14
13
|
}
|
|
@@ -17,18 +16,18 @@ export async function defaultProjectDirs(
|
|
|
17
16
|
projectRootAbs: string,
|
|
18
17
|
): Promise<Readonly<DankDirectories>> {
|
|
19
18
|
if (!isAbsolute(projectRootAbs)) {
|
|
20
|
-
throw Error()
|
|
19
|
+
throw Error('must use an absolute project root path')
|
|
20
|
+
}
|
|
21
|
+
if ((await realpath(projectRootAbs)) !== projectRootAbs) {
|
|
22
|
+
throw Error('must use a real project root path')
|
|
21
23
|
}
|
|
22
|
-
const projectResolved = await realpath(projectRootAbs)
|
|
23
24
|
const pages = 'pages'
|
|
24
|
-
const pagesResolved = join(projectResolved, pages)
|
|
25
25
|
return Object.freeze({
|
|
26
26
|
buildRoot: 'build',
|
|
27
27
|
buildDist: join('build', 'dist'),
|
|
28
28
|
buildWatch: join('build', 'watch'),
|
|
29
29
|
pages,
|
|
30
|
-
|
|
31
|
-
projectResolved,
|
|
30
|
+
pagesAbs: join(projectRootAbs, pages),
|
|
32
31
|
projectRootAbs,
|
|
33
32
|
public: 'public',
|
|
34
33
|
})
|
|
@@ -63,8 +62,8 @@ export class Resolver {
|
|
|
63
62
|
|
|
64
63
|
// `p` is expected to be a relative path resolvable from the project dir
|
|
65
64
|
isProjectSubpathInPagesDir(p: string): boolean {
|
|
66
|
-
return resolve(join(this.#dirs.
|
|
67
|
-
this.#dirs.
|
|
65
|
+
return resolve(join(this.#dirs.projectRootAbs, p)).startsWith(
|
|
66
|
+
this.#dirs.pagesAbs,
|
|
68
67
|
)
|
|
69
68
|
}
|
|
70
69
|
|
package/lib/http.ts
CHANGED
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
WebsiteManifest,
|
|
19
19
|
WebsiteRegistry,
|
|
20
20
|
} from './registry.ts'
|
|
21
|
-
import type {
|
|
21
|
+
import type { DevServices } from './services.ts'
|
|
22
22
|
|
|
23
23
|
export type FrontendFetcher = (
|
|
24
24
|
url: URL,
|
|
@@ -33,7 +33,7 @@ export function startWebServer(
|
|
|
33
33
|
dirs: DankDirectories,
|
|
34
34
|
urlRewriteProvider: UrlRewriteProvider,
|
|
35
35
|
frontendFetcher: FrontendFetcher,
|
|
36
|
-
|
|
36
|
+
devServices: DevServices,
|
|
37
37
|
) {
|
|
38
38
|
const serverAddress = 'http://localhost:' + port
|
|
39
39
|
const handler = (req: IncomingMessage, res: ServerResponse) => {
|
|
@@ -47,7 +47,7 @@ export function startWebServer(
|
|
|
47
47
|
req,
|
|
48
48
|
url,
|
|
49
49
|
headers,
|
|
50
|
-
|
|
50
|
+
devServices,
|
|
51
51
|
flags,
|
|
52
52
|
dirs,
|
|
53
53
|
urlRewriteProvider,
|
|
@@ -69,7 +69,7 @@ async function onNotFound(
|
|
|
69
69
|
req: IncomingMessage,
|
|
70
70
|
url: URL,
|
|
71
71
|
headers: Headers,
|
|
72
|
-
|
|
72
|
+
devServices: DevServices,
|
|
73
73
|
flags: DankFlags,
|
|
74
74
|
dirs: DankDirectories,
|
|
75
75
|
urlRewriteProvider: UrlRewriteProvider,
|
|
@@ -87,7 +87,7 @@ async function onNotFound(
|
|
|
87
87
|
return
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
|
-
const fetchResponse = await tryHttpServices(req, url, headers,
|
|
90
|
+
const fetchResponse = await tryHttpServices(req, url, headers, devServices)
|
|
91
91
|
if (fetchResponse) {
|
|
92
92
|
sendFetchResponse(res, fetchResponse)
|
|
93
93
|
} else {
|
|
@@ -131,14 +131,13 @@ async function tryHttpServices(
|
|
|
131
131
|
req: IncomingMessage,
|
|
132
132
|
url: URL,
|
|
133
133
|
headers: Headers,
|
|
134
|
-
|
|
134
|
+
devServices: DevServices,
|
|
135
135
|
): Promise<Response | null> {
|
|
136
136
|
if (url.pathname.startsWith('/.well-known/')) {
|
|
137
137
|
return null
|
|
138
138
|
}
|
|
139
139
|
const body = await collectReqBody(req)
|
|
140
|
-
const
|
|
141
|
-
for (const httpService of running) {
|
|
140
|
+
for (const httpService of devServices.httpServices) {
|
|
142
141
|
const proxyUrl = new URL(url)
|
|
143
142
|
proxyUrl.port = `${httpService.port}`
|
|
144
143
|
try {
|
|
@@ -198,7 +197,7 @@ export function createBuiltDistFilesFetcher(
|
|
|
198
197
|
if (manifest.pageUrls.has(url.pathname)) {
|
|
199
198
|
streamFile(
|
|
200
199
|
join(
|
|
201
|
-
dirs.
|
|
200
|
+
dirs.projectRootAbs,
|
|
202
201
|
dirs.buildDist,
|
|
203
202
|
url.pathname,
|
|
204
203
|
'index.html',
|
|
@@ -207,7 +206,7 @@ export function createBuiltDistFilesFetcher(
|
|
|
207
206
|
)
|
|
208
207
|
} else if (manifest.files.has(url.pathname)) {
|
|
209
208
|
streamFile(
|
|
210
|
-
join(dirs.
|
|
209
|
+
join(dirs.projectRootAbs, dirs.buildDist, url.pathname),
|
|
211
210
|
res,
|
|
212
211
|
)
|
|
213
212
|
} else {
|
package/lib/serve.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
startWebServer,
|
|
14
14
|
} from './http.ts'
|
|
15
15
|
import { WebsiteRegistry, type UrlRewrite } from './registry.ts'
|
|
16
|
-
import {
|
|
16
|
+
import { DevServices, type ManagedServiceLabel } from './services.ts'
|
|
17
17
|
import { watch } from './watch.ts'
|
|
18
18
|
|
|
19
19
|
let c: ResolvedDankConfig
|
|
@@ -21,20 +21,18 @@ let c: ResolvedDankConfig
|
|
|
21
21
|
export async function serveWebsite(): Promise<never> {
|
|
22
22
|
c = await loadConfig('serve', process.cwd())
|
|
23
23
|
await rm(c.dirs.buildRoot, { force: true, recursive: true })
|
|
24
|
-
const abortController = new AbortController()
|
|
25
|
-
process.once('exit', () => abortController.abort())
|
|
26
24
|
if (c.flags.preview) {
|
|
27
|
-
await startPreviewMode(
|
|
25
|
+
await startPreviewMode()
|
|
28
26
|
} else {
|
|
29
|
-
await startDevMode(
|
|
27
|
+
await startDevMode()
|
|
30
28
|
}
|
|
31
29
|
return new Promise(() => {})
|
|
32
30
|
}
|
|
33
31
|
|
|
34
|
-
async function startPreviewMode(
|
|
32
|
+
async function startPreviewMode() {
|
|
35
33
|
const manifest = await buildWebsite(c)
|
|
36
34
|
const frontend = createBuiltDistFilesFetcher(c.dirs, manifest)
|
|
37
|
-
const devServices =
|
|
35
|
+
const devServices = launchDevServices()
|
|
38
36
|
const urlRewrites: Array<UrlRewrite> = Object.keys(c.pages)
|
|
39
37
|
.sort()
|
|
40
38
|
.map(url => {
|
|
@@ -50,8 +48,17 @@ async function startPreviewMode(signal: AbortSignal) {
|
|
|
50
48
|
c.dirs,
|
|
51
49
|
{ urlRewrites },
|
|
52
50
|
frontend,
|
|
53
|
-
devServices
|
|
51
|
+
devServices,
|
|
54
52
|
)
|
|
53
|
+
const controller = new AbortController()
|
|
54
|
+
watch('dank.config.ts', controller.signal, async filename => {
|
|
55
|
+
console.log(filename, 'was updated!')
|
|
56
|
+
console.log(
|
|
57
|
+
'config updates are not hot reloaded during `dank serve --preview`',
|
|
58
|
+
)
|
|
59
|
+
console.log('restart DANK to reload configuration')
|
|
60
|
+
controller.abort()
|
|
61
|
+
})
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
type BuildContextState =
|
|
@@ -61,12 +68,12 @@ type BuildContextState =
|
|
|
61
68
|
| 'disposing'
|
|
62
69
|
| null
|
|
63
70
|
|
|
64
|
-
async function startDevMode(
|
|
71
|
+
async function startDevMode() {
|
|
65
72
|
const registry = new WebsiteRegistry(c)
|
|
66
73
|
await mkdir(c.dirs.buildWatch, { recursive: true })
|
|
67
74
|
let buildContext: BuildContextState = null
|
|
68
75
|
|
|
69
|
-
watch('dank.config.ts',
|
|
76
|
+
watch('dank.config.ts', async filename => {
|
|
70
77
|
LOG({
|
|
71
78
|
realm: 'serve',
|
|
72
79
|
message: 'config watch event',
|
|
@@ -80,10 +87,10 @@ async function startDevMode(signal: AbortSignal) {
|
|
|
80
87
|
return
|
|
81
88
|
}
|
|
82
89
|
registry.configSync()
|
|
83
|
-
|
|
90
|
+
devServices.update(c.services)
|
|
84
91
|
})
|
|
85
92
|
|
|
86
|
-
watch(c.dirs.pages,
|
|
93
|
+
watch(c.dirs.pages, { recursive: true }, filename => {
|
|
87
94
|
LOG({
|
|
88
95
|
realm: 'serve',
|
|
89
96
|
message: 'pages dir watch event',
|
|
@@ -163,15 +170,8 @@ async function startDevMode(signal: AbortSignal) {
|
|
|
163
170
|
resetBuildContext()
|
|
164
171
|
|
|
165
172
|
const frontend = createDevServeFilesFetcher(c.esbuildPort, c.dirs, registry)
|
|
166
|
-
const devServices =
|
|
167
|
-
startWebServer(
|
|
168
|
-
c.dankPort,
|
|
169
|
-
c.flags,
|
|
170
|
-
c.dirs,
|
|
171
|
-
registry,
|
|
172
|
-
frontend,
|
|
173
|
-
devServices.http,
|
|
174
|
-
)
|
|
173
|
+
const devServices = launchDevServices()
|
|
174
|
+
startWebServer(c.dankPort, c.flags, c.dirs, registry, frontend, devServices)
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
async function startEsbuildWatch(
|
|
@@ -220,3 +220,47 @@ async function writeHtml(html: HtmlEntrypoint, output: string) {
|
|
|
220
220
|
})
|
|
221
221
|
await writeFile(path, output)
|
|
222
222
|
}
|
|
223
|
+
|
|
224
|
+
function launchDevServices(): DevServices {
|
|
225
|
+
const services = new DevServices(c.services)
|
|
226
|
+
services.on('error', (label, cause) =>
|
|
227
|
+
console.log(formatServiceLabel(label), 'errored:', cause),
|
|
228
|
+
)
|
|
229
|
+
services.on('exit', (label, code) => {
|
|
230
|
+
if (code) {
|
|
231
|
+
console.log(formatServiceLabel(label), 'exited', code)
|
|
232
|
+
} else {
|
|
233
|
+
console.log(formatServiceLabel(label), 'exited')
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
services.on('launch', label =>
|
|
237
|
+
console.log(formatServiceLabel(label), 'starting'),
|
|
238
|
+
)
|
|
239
|
+
services.on('stdout', (label, output) =>
|
|
240
|
+
printServiceOutput(label, 32, output),
|
|
241
|
+
)
|
|
242
|
+
services.on('stderr', (label, output) =>
|
|
243
|
+
printServiceOutput(label, 31, output),
|
|
244
|
+
)
|
|
245
|
+
return services
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function formatServiceLabel(label: ManagedServiceLabel): string {
|
|
249
|
+
return `| \u001b[2m${label.cwd}\u001b[22m ${label.command} |`
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function formatServiceOutputLabel(
|
|
253
|
+
label: ManagedServiceLabel,
|
|
254
|
+
color: 31 | 32,
|
|
255
|
+
): string {
|
|
256
|
+
return `\u001b[${color}m${formatServiceLabel(label)}\u001b[39m`
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function printServiceOutput(
|
|
260
|
+
label: ManagedServiceLabel,
|
|
261
|
+
color: 31 | 32,
|
|
262
|
+
output: Array<string>,
|
|
263
|
+
) {
|
|
264
|
+
const formattedLabel = formatServiceOutputLabel(label, color)
|
|
265
|
+
for (const line of output) console.log(formattedLabel, line)
|
|
266
|
+
}
|