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