@eighty4/dank 0.0.3 → 0.0.4-1
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/esbuild.js +8 -5
- package/lib/build.ts +133 -72
- package/lib/{tag.ts → build_tag.ts} +3 -3
- package/lib/dank.ts +147 -11
- package/lib/define.ts +4 -5
- package/lib/esbuild.ts +239 -60
- package/lib/flags.ts +154 -10
- package/lib/html.ts +320 -116
- package/lib/http.ts +195 -47
- package/lib/metadata.ts +309 -0
- package/lib/public.ts +21 -13
- package/lib/serve.ts +205 -144
- package/lib/services.ts +28 -4
- package/lib_js/build.js +82 -57
- package/lib_js/{tag.js → build_tag.js} +2 -3
- package/lib_js/dank.js +73 -5
- package/lib_js/define.js +3 -5
- package/lib_js/esbuild.js +166 -54
- package/lib_js/flags.js +123 -8
- package/lib_js/html.js +197 -87
- package/lib_js/http.js +111 -30
- package/lib_js/metadata.js +210 -0
- package/lib_js/public.js +19 -11
- package/lib_js/serve.js +135 -110
- package/lib_js/services.js +13 -2
- package/lib_types/dank.d.ts +18 -1
- package/package.json +7 -1
- package/lib/manifest.ts +0 -61
- package/lib_js/manifest.js +0 -37
package/lib/html.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import EventEmitter from 'node:events'
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
2
3
|
import { dirname, join, relative } from 'node:path'
|
|
3
4
|
import { extname } from 'node:path/posix'
|
|
4
5
|
import {
|
|
@@ -8,164 +9,318 @@ import {
|
|
|
8
9
|
parseFragment,
|
|
9
10
|
serialize,
|
|
10
11
|
} from 'parse5'
|
|
12
|
+
import type { EntryPoint } from './esbuild.ts'
|
|
13
|
+
import type { DankBuild } from './flags.ts'
|
|
14
|
+
import type { Resolver } from './metadata.ts'
|
|
11
15
|
|
|
12
16
|
type CommentNode = DefaultTreeAdapterTypes.CommentNode
|
|
13
17
|
type Document = DefaultTreeAdapterTypes.Document
|
|
18
|
+
type DocumentFragment = DefaultTreeAdapterTypes.DocumentFragment
|
|
14
19
|
type Element = DefaultTreeAdapterTypes.Element
|
|
15
20
|
type ParentNode = DefaultTreeAdapterTypes.ParentNode
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
type CollectedImports = {
|
|
23
|
+
partials: Array<PartialReference>
|
|
24
|
+
scripts: Array<ImportedScript>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type PartialReference = {
|
|
28
|
+
commentNode: CommentNode
|
|
29
|
+
// path within pages dir omitting pages/ segment
|
|
30
|
+
fsPath: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type PartialContent = PartialReference & {
|
|
34
|
+
fragment: DocumentFragment
|
|
35
|
+
imports: CollectedImports
|
|
36
|
+
// todo recursive partials?
|
|
37
|
+
// partials: Array<PartialContent>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type ImportedScript = {
|
|
18
41
|
type: 'script' | 'style'
|
|
19
42
|
href: string
|
|
20
43
|
elem: Element
|
|
21
|
-
|
|
22
|
-
out: string
|
|
44
|
+
entrypoint: EntryPoint
|
|
23
45
|
}
|
|
24
46
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
): Promise<HtmlEntrypoint> {
|
|
35
|
-
let html: string
|
|
36
|
-
try {
|
|
37
|
-
html = await readFile(fsPath, 'utf-8')
|
|
38
|
-
} catch (e) {
|
|
39
|
-
console.log(`\u001b[31merror:\u001b[0m`, fsPath, 'does not exist')
|
|
40
|
-
process.exit(1)
|
|
41
|
-
}
|
|
42
|
-
return new HtmlEntrypoint(urlPath, html, fsPath)
|
|
43
|
-
}
|
|
47
|
+
export type HtmlDecoration = {
|
|
48
|
+
type: 'script'
|
|
49
|
+
js: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// implicitly impl'd by WebsiteRegistry
|
|
53
|
+
export type HtmlHrefs = {
|
|
54
|
+
mappedHref(lookup: string): string
|
|
55
|
+
}
|
|
44
56
|
|
|
45
|
-
|
|
57
|
+
export type HtmlEntrypointEvents = {
|
|
58
|
+
// Dispatched from fs watch to notify HtmlEntrypoint of changes to HtmlEntrypoint.#fsPath
|
|
59
|
+
// Optional parameter `partial` notifies the page when a partial of the page has changed
|
|
60
|
+
change: [partial?: string]
|
|
61
|
+
// Dispatched from HtmlEntrypoint to notify `dank serve` of changes to esbuild entrypoints
|
|
62
|
+
// Parameter `entrypoints` is the esbuild mappings of the input and output paths
|
|
63
|
+
entrypoints: [entrypoints: Array<EntryPoint>]
|
|
64
|
+
// Dispatched from HtmlEntrypoint to notify when new HtmlEntrypoint.#document output is ready for write
|
|
65
|
+
// Parameter `html` is the updated html content of the page ready to be output to the build dir
|
|
66
|
+
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
|
+
}
|
|
75
|
+
|
|
76
|
+
export class HtmlEntrypoint extends EventEmitter<HtmlEntrypointEvents> {
|
|
77
|
+
#build: DankBuild
|
|
78
|
+
#decorations?: Array<HtmlDecoration>
|
|
79
|
+
#document: Document = defaultTreeAdapter.createDocument()
|
|
80
|
+
// todo cache entrypoints set for quicker diffing
|
|
81
|
+
// #entrypoints: Set<string> = new Set()
|
|
82
|
+
// path within pages dir omitting pages/ segment
|
|
46
83
|
#fsPath: string
|
|
47
|
-
#partials: Array<
|
|
84
|
+
#partials: Array<PartialContent> = []
|
|
85
|
+
#resolver: Resolver
|
|
48
86
|
#scripts: Array<ImportedScript> = []
|
|
87
|
+
#update: Object = Object()
|
|
49
88
|
#url: string
|
|
50
89
|
|
|
51
|
-
constructor(
|
|
90
|
+
constructor(
|
|
91
|
+
build: DankBuild,
|
|
92
|
+
resolver: Resolver,
|
|
93
|
+
url: string,
|
|
94
|
+
fsPath: string,
|
|
95
|
+
decorations?: Array<HtmlDecoration>,
|
|
96
|
+
) {
|
|
97
|
+
super({ captureRejections: true })
|
|
98
|
+
this.#build = build
|
|
99
|
+
this.#resolver = resolver
|
|
100
|
+
this.#decorations = decorations
|
|
52
101
|
this.#url = url
|
|
53
|
-
this.#document = parse(html)
|
|
54
102
|
this.#fsPath = fsPath
|
|
103
|
+
this.on('change', this.#onChange)
|
|
104
|
+
this.emit('change')
|
|
55
105
|
}
|
|
56
106
|
|
|
57
|
-
|
|
58
|
-
this.#
|
|
59
|
-
await this.#injectPartials()
|
|
107
|
+
get fsPath(): string {
|
|
108
|
+
return this.#fsPath
|
|
60
109
|
}
|
|
61
110
|
|
|
62
|
-
|
|
63
|
-
this.#
|
|
64
|
-
return this.#scripts
|
|
111
|
+
get url(): string {
|
|
112
|
+
return this.#url
|
|
65
113
|
}
|
|
66
114
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// todo if partial changes, hot swap content in page
|
|
128
|
+
#onChange = async (_partial?: string) => {
|
|
129
|
+
const update = (this.#update = Object())
|
|
130
|
+
const html = await this.#html()
|
|
131
|
+
const document = parse(html)
|
|
132
|
+
const imports: CollectedImports = {
|
|
133
|
+
partials: [],
|
|
134
|
+
scripts: [],
|
|
135
|
+
}
|
|
136
|
+
this.#collectImports(document, imports)
|
|
137
|
+
const partials = await this.#resolvePartialContent(imports.partials)
|
|
138
|
+
if (update !== this.#update) {
|
|
139
|
+
// another update has started so aborting this one
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
this.#addDecorations(document)
|
|
143
|
+
this.#update = update
|
|
144
|
+
this.#document = document
|
|
145
|
+
this.#partials = partials
|
|
146
|
+
this.#scripts = imports.scripts
|
|
147
|
+
const entrypoints = mergeEntrypoints(
|
|
148
|
+
imports,
|
|
149
|
+
...partials.map(p => p.imports),
|
|
150
|
+
)
|
|
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
|
+
)
|
|
157
|
+
if (this.listenerCount('output')) {
|
|
158
|
+
this.emit('output', this.output())
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
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
|
|
165
|
+
async #resolvePartialContent(
|
|
166
|
+
partials: Array<PartialReference>,
|
|
167
|
+
): Promise<Array<PartialContent>> {
|
|
168
|
+
return await Promise.all(
|
|
169
|
+
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
|
+
)
|
|
175
|
+
const fragment = parseFragment(html)
|
|
176
|
+
const imports: CollectedImports = {
|
|
177
|
+
partials: [],
|
|
178
|
+
scripts: [],
|
|
179
|
+
}
|
|
180
|
+
this.#collectImports(fragment, imports, node => {
|
|
181
|
+
this.#rewritePartialRelativePaths(node, p.fsPath)
|
|
182
|
+
})
|
|
183
|
+
if (imports.partials.length) {
|
|
184
|
+
// todo recursive partials?
|
|
185
|
+
// await this.#resolvePartialContent(imports.partials)
|
|
186
|
+
errorExit(
|
|
187
|
+
`partial ${p.fsPath} cannot recursively import partials`,
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
const content: PartialContent = {
|
|
191
|
+
...p,
|
|
192
|
+
fragment,
|
|
193
|
+
imports,
|
|
80
194
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
195
|
+
return content
|
|
196
|
+
}),
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// rewrite hrefs in a partial to be relative to the html entrypoint instead of the partial
|
|
201
|
+
#rewritePartialRelativePaths(elem: Element, partialPath: string) {
|
|
202
|
+
let rewritePath: 'src' | 'href' | null = null
|
|
203
|
+
if (elem.nodeName === 'script') {
|
|
204
|
+
rewritePath = 'src'
|
|
205
|
+
} else if (
|
|
206
|
+
elem.nodeName === 'link' &&
|
|
207
|
+
hasAttr(elem, 'rel', 'stylesheet')
|
|
208
|
+
) {
|
|
209
|
+
rewritePath = 'href'
|
|
210
|
+
}
|
|
211
|
+
if (rewritePath !== null) {
|
|
212
|
+
const attr = getAttr(elem, rewritePath)
|
|
213
|
+
if (attr) {
|
|
214
|
+
attr.value = join(
|
|
215
|
+
relative(dirname(this.#fsPath), dirname(partialPath)),
|
|
216
|
+
attr.value,
|
|
217
|
+
)
|
|
85
218
|
}
|
|
86
219
|
}
|
|
87
220
|
}
|
|
88
221
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
+
}
|
|
100
245
|
}
|
|
101
246
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
247
|
+
output(hrefs?: HtmlHrefs): string {
|
|
248
|
+
this.#injectPartials()
|
|
249
|
+
this.#rewriteHrefs(hrefs)
|
|
250
|
+
return serialize(this.#document)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// rewrites hrefs to content hashed urls
|
|
254
|
+
// call without hrefs to rewrite tsx? ext to js
|
|
255
|
+
#rewriteHrefs(hrefs?: HtmlHrefs) {
|
|
256
|
+
rewriteHrefs(this.#scripts, hrefs)
|
|
257
|
+
for (const partial of this.#partials) {
|
|
258
|
+
rewriteHrefs(partial.imports.scripts, hrefs)
|
|
259
|
+
}
|
|
107
260
|
}
|
|
108
261
|
|
|
109
262
|
async #injectPartials() {
|
|
110
|
-
for (const commentNode of this.#partials) {
|
|
111
|
-
|
|
112
|
-
.
|
|
113
|
-
|
|
114
|
-
|
|
263
|
+
for (const { commentNode, fragment } of this.#partials) {
|
|
264
|
+
if (!this.#build.production) {
|
|
265
|
+
defaultTreeAdapter.insertBefore(
|
|
266
|
+
commentNode.parentNode!,
|
|
267
|
+
defaultTreeAdapter.createCommentNode(commentNode.data),
|
|
268
|
+
commentNode,
|
|
269
|
+
)
|
|
270
|
+
}
|
|
115
271
|
for (const node of fragment.childNodes) {
|
|
116
|
-
if (node.nodeName === 'script') {
|
|
117
|
-
this.#rewritePathFromPartial(pp, node, 'src')
|
|
118
|
-
} else if (
|
|
119
|
-
node.nodeName === 'link' &&
|
|
120
|
-
hasAttr(node, 'rel', 'stylesheet')
|
|
121
|
-
) {
|
|
122
|
-
this.#rewritePathFromPartial(pp, node, 'href')
|
|
123
|
-
}
|
|
124
272
|
defaultTreeAdapter.insertBefore(
|
|
125
273
|
commentNode.parentNode!,
|
|
126
274
|
node,
|
|
127
275
|
commentNode,
|
|
128
276
|
)
|
|
129
277
|
}
|
|
130
|
-
|
|
278
|
+
if (this.#build.production) {
|
|
279
|
+
defaultTreeAdapter.detachNode(commentNode)
|
|
280
|
+
}
|
|
131
281
|
}
|
|
132
282
|
}
|
|
133
283
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
elem: Element,
|
|
138
|
-
attrName: 'href' | 'src',
|
|
284
|
+
#collectImports(
|
|
285
|
+
node: ParentNode,
|
|
286
|
+
collection: CollectedImports,
|
|
287
|
+
forEach?: (elem: Element) => void,
|
|
139
288
|
) {
|
|
140
|
-
const attr = getAttr(elem, attrName)
|
|
141
|
-
if (attr) {
|
|
142
|
-
attr.value = join(
|
|
143
|
-
relative(dirname(this.#fsPath), dirname(pp)),
|
|
144
|
-
attr.value,
|
|
145
|
-
)
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
#collectPartials(node: ParentNode) {
|
|
150
289
|
for (const childNode of node.childNodes) {
|
|
290
|
+
if (forEach && 'tagName' in childNode) {
|
|
291
|
+
forEach(childNode)
|
|
292
|
+
}
|
|
151
293
|
if (childNode.nodeName === '#comment' && 'data' in childNode) {
|
|
152
|
-
|
|
153
|
-
|
|
294
|
+
const partialMatch = childNode.data.match(/\{\{(?<pp>.+)\}\}/)
|
|
295
|
+
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
|
+
}
|
|
311
|
+
collection.partials.push({
|
|
312
|
+
fsPath: partialPath,
|
|
313
|
+
commentNode: childNode,
|
|
314
|
+
})
|
|
154
315
|
}
|
|
155
|
-
} else if ('
|
|
156
|
-
this.#collectPartials(childNode)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
#collectScripts(node: ParentNode) {
|
|
162
|
-
for (const childNode of node.childNodes) {
|
|
163
|
-
if (childNode.nodeName === 'script') {
|
|
316
|
+
} else if (childNode.nodeName === 'script') {
|
|
164
317
|
const srcAttr = childNode.attrs.find(
|
|
165
318
|
attr => attr.name === 'src',
|
|
166
319
|
)
|
|
167
320
|
if (srcAttr) {
|
|
168
|
-
|
|
321
|
+
collection.scripts.push(
|
|
322
|
+
this.#parseImport('script', srcAttr.value, childNode),
|
|
323
|
+
)
|
|
169
324
|
}
|
|
170
325
|
} else if (
|
|
171
326
|
childNode.nodeName === 'link' &&
|
|
@@ -173,30 +328,43 @@ export class HtmlEntrypoint {
|
|
|
173
328
|
) {
|
|
174
329
|
const hrefAttr = getAttr(childNode, 'href')
|
|
175
330
|
if (hrefAttr) {
|
|
176
|
-
|
|
331
|
+
collection.scripts.push(
|
|
332
|
+
this.#parseImport('style', hrefAttr.value, childNode),
|
|
333
|
+
)
|
|
177
334
|
}
|
|
178
335
|
} else if ('childNodes' in childNode) {
|
|
179
|
-
this.#
|
|
336
|
+
this.#collectImports(childNode, collection)
|
|
180
337
|
}
|
|
181
338
|
}
|
|
182
339
|
}
|
|
183
340
|
|
|
184
|
-
#
|
|
185
|
-
|
|
186
|
-
|
|
341
|
+
#parseImport(
|
|
342
|
+
type: ImportedScript['type'],
|
|
343
|
+
href: string,
|
|
344
|
+
elem: Element,
|
|
345
|
+
): ImportedScript {
|
|
346
|
+
const inPath = join(this.#build.dirs.pages, dirname(this.#fsPath), href)
|
|
347
|
+
if (!this.#resolver.isProjectSubpathInPagesDir(inPath)) {
|
|
348
|
+
errorExit(
|
|
349
|
+
`href ${href} in webpage ${this.#fsPath} cannot reference sources outside of the pages directory`,
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
let outPath = join(dirname(this.#fsPath), href)
|
|
187
353
|
if (type === 'script' && !outPath.endsWith('.js')) {
|
|
188
354
|
outPath = outPath.replace(
|
|
189
355
|
new RegExp(extname(outPath).substring(1) + '$'),
|
|
190
356
|
'js',
|
|
191
357
|
)
|
|
192
358
|
}
|
|
193
|
-
|
|
359
|
+
return {
|
|
194
360
|
type,
|
|
195
361
|
href,
|
|
196
362
|
elem,
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
363
|
+
entrypoint: {
|
|
364
|
+
in: inPath,
|
|
365
|
+
out: outPath,
|
|
366
|
+
},
|
|
367
|
+
}
|
|
200
368
|
}
|
|
201
369
|
}
|
|
202
370
|
|
|
@@ -207,3 +375,39 @@ function getAttr(elem: Element, name: string) {
|
|
|
207
375
|
function hasAttr(elem: Element, name: string, value: string): boolean {
|
|
208
376
|
return elem.attrs.some(attr => attr.name === name && attr.value === value)
|
|
209
377
|
}
|
|
378
|
+
|
|
379
|
+
function mergeEntrypoints(
|
|
380
|
+
...imports: Array<CollectedImports>
|
|
381
|
+
): Array<EntryPoint> {
|
|
382
|
+
const entrypoints: Array<EntryPoint> = []
|
|
383
|
+
for (const { scripts } of imports) {
|
|
384
|
+
for (const script of scripts) {
|
|
385
|
+
entrypoints.push(script.entrypoint)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return entrypoints
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function rewriteHrefs(scripts: Array<ImportedScript>, hrefs?: HtmlHrefs) {
|
|
392
|
+
for (const { elem, entrypoint, type } of scripts) {
|
|
393
|
+
const rewriteTo = hrefs ? hrefs.mappedHref(entrypoint.in) : null
|
|
394
|
+
if (type === 'script') {
|
|
395
|
+
if (
|
|
396
|
+
entrypoint.in.endsWith('.tsx') ||
|
|
397
|
+
entrypoint.in.endsWith('.ts')
|
|
398
|
+
) {
|
|
399
|
+
elem.attrs.find(attr => attr.name === 'src')!.value =
|
|
400
|
+
rewriteTo || `/${entrypoint.out}`
|
|
401
|
+
}
|
|
402
|
+
} else if (type === 'style') {
|
|
403
|
+
elem.attrs.find(attr => attr.name === 'href')!.value =
|
|
404
|
+
rewriteTo || `/${entrypoint.out}`
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
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)
|
|
413
|
+
}
|