@eighty4/dank 0.0.1-2 → 0.0.1-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/esbuild.js +77 -0
- package/lib/esbuild.ts +4 -1
- package/lib/html.ts +13 -0
- package/lib/serve.ts +8 -2
- package/lib_js/esbuild.js +4 -1
- package/lib_js/html.js +6 -0
- package/lib_js/serve.js +4 -2
- package/package.json +3 -2
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
new EventSource('http://127.0.0.1:2999/esbuild').addEventListener('change', (e) => {
|
|
2
|
+
const change = JSON.parse(e.data);
|
|
3
|
+
const cssUpdates = change.updated.filter(p => p.endsWith('.css'));
|
|
4
|
+
if (cssUpdates.length) {
|
|
5
|
+
console.log('esbuild css updates', cssUpdates);
|
|
6
|
+
const cssLinks = {};
|
|
7
|
+
for (const elem of document.getElementsByTagName('link')) {
|
|
8
|
+
if (elem.getAttribute('rel') === 'stylesheet') {
|
|
9
|
+
const url = new URL(elem.href);
|
|
10
|
+
if ((url.host = location.host)) {
|
|
11
|
+
cssLinks[url.pathname] = elem;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
let swappedCss = false;
|
|
16
|
+
for (const cssUpdate of cssUpdates) {
|
|
17
|
+
const cssLink = cssLinks[cssUpdate];
|
|
18
|
+
if (cssLink) {
|
|
19
|
+
const next = cssLink.cloneNode();
|
|
20
|
+
next.href = `${cssUpdate}?${Math.random().toString(36).slice(2)}`;
|
|
21
|
+
next.onload = () => cssLink.remove();
|
|
22
|
+
cssLink.parentNode.insertBefore(next, cssLink.nextSibling);
|
|
23
|
+
swappedCss = true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (swappedCss) {
|
|
27
|
+
addCssUpdateIndicator();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (cssUpdates.length < change.updated.length) {
|
|
31
|
+
const jsUpdates = change.updated.filter(p => !p.endsWith('.css'));
|
|
32
|
+
const jsScripts = new Set();
|
|
33
|
+
for (const elem of document.getElementsByTagName('script')) {
|
|
34
|
+
if (elem.src.length) {
|
|
35
|
+
const url = new URL(elem.src);
|
|
36
|
+
if ((url.host = location.host)) {
|
|
37
|
+
jsScripts.add(url.pathname);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (jsUpdates.some(jsUpdate => jsScripts.has(jsUpdate))) {
|
|
42
|
+
console.log('esbuild js updates require reload');
|
|
43
|
+
addJsReloadIndicator();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
function addCssUpdateIndicator() {
|
|
48
|
+
const indicator = createUpdateIndicator('green', '9999');
|
|
49
|
+
indicator.style.transition = 'opacity ease-in-out .38s';
|
|
50
|
+
indicator.style.opacity = '0';
|
|
51
|
+
indicator.ontransitionend = () => {
|
|
52
|
+
if (indicator.style.opacity === '1') {
|
|
53
|
+
indicator.style.opacity = '0';
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
indicator.remove();
|
|
57
|
+
indicator.onload = null;
|
|
58
|
+
indicator.ontransitionend = null;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
document.body.appendChild(indicator);
|
|
62
|
+
setTimeout(() => (indicator.style.opacity = '1'), 0);
|
|
63
|
+
}
|
|
64
|
+
function addJsReloadIndicator() {
|
|
65
|
+
document.body.appendChild(createUpdateIndicator('orange', '9000'));
|
|
66
|
+
}
|
|
67
|
+
function createUpdateIndicator(color, zIndex) {
|
|
68
|
+
const indicator = document.createElement('div');
|
|
69
|
+
indicator.style.border = '6px dashed ' + color;
|
|
70
|
+
indicator.style.zIndex = zIndex;
|
|
71
|
+
indicator.style.position = 'fixed';
|
|
72
|
+
indicator.style.top = indicator.style.left = '1px';
|
|
73
|
+
indicator.style.height = indicator.style.width = 'calc(100% - 2px)';
|
|
74
|
+
indicator.style.boxSizing = 'border-box';
|
|
75
|
+
return indicator;
|
|
76
|
+
}
|
|
77
|
+
export {};
|
package/lib/esbuild.ts
CHANGED
|
@@ -32,7 +32,7 @@ export async function esbuildDevContext(
|
|
|
32
32
|
return await esbuild.context({
|
|
33
33
|
define,
|
|
34
34
|
entryNames: '[dir]/[name]',
|
|
35
|
-
entryPoints,
|
|
35
|
+
entryPoints: removeEntryPointOutExt(entryPoints),
|
|
36
36
|
outdir,
|
|
37
37
|
...webpageBuildOptions,
|
|
38
38
|
})
|
|
@@ -54,6 +54,9 @@ export async function esbuildWebpages(
|
|
|
54
54
|
return buildResult.metafile
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// esbuild will append the .js or .css to output filenames
|
|
58
|
+
// keeping extension on entryPoints data for consistency
|
|
59
|
+
// and removing and mapping entryPoints to pass to esbuild
|
|
57
60
|
function removeEntryPointOutExt(
|
|
58
61
|
entryPoints: Array<{ in: string; out: string }>,
|
|
59
62
|
) {
|
package/lib/html.ts
CHANGED
|
@@ -85,6 +85,19 @@ export class HtmlEntrypoint {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
appendScript(clientJS: string) {
|
|
89
|
+
const scriptNode = parseFragment(
|
|
90
|
+
`<script type="module">${clientJS}</script>`,
|
|
91
|
+
).childNodes[0]
|
|
92
|
+
const htmlNode = this.#document.childNodes.find(
|
|
93
|
+
node => node.nodeName === 'html',
|
|
94
|
+
) as ParentNode
|
|
95
|
+
const headNode = htmlNode.childNodes.find(
|
|
96
|
+
node => node.nodeName === 'head',
|
|
97
|
+
) as ParentNode | undefined
|
|
98
|
+
defaultTreeAdapter.appendChild(headNode || htmlNode, scriptNode)
|
|
99
|
+
}
|
|
100
|
+
|
|
88
101
|
async writeTo(buildDir: string): Promise<void> {
|
|
89
102
|
await writeFile(
|
|
90
103
|
join(buildDir, this.#url, 'index.html'),
|
package/lib/serve.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { mkdir, rm } from 'node:fs/promises'
|
|
2
|
-
import { join } from 'node:path'
|
|
1
|
+
import { mkdir, readFile, rm } from 'node:fs/promises'
|
|
2
|
+
import { join, resolve } from 'node:path'
|
|
3
3
|
import { buildWebsite } from './build.ts'
|
|
4
4
|
import type { DankConfig } from './dank.ts'
|
|
5
5
|
import { createGlobalDefinitions } from './define.ts'
|
|
@@ -47,6 +47,11 @@ async function startEsbuildWatch(c: DankConfig): Promise<{ port: number }> {
|
|
|
47
47
|
await mkdir(watchDir, { recursive: true })
|
|
48
48
|
await copyAssets(watchDir)
|
|
49
49
|
|
|
50
|
+
const clientJS = await readFile(
|
|
51
|
+
resolve(import.meta.dirname, join('..', 'client', 'esbuild.js')),
|
|
52
|
+
'utf-8',
|
|
53
|
+
)
|
|
54
|
+
|
|
50
55
|
const entryPointUrls: Set<string> = new Set()
|
|
51
56
|
const entryPoints: Array<{ in: string; out: string }> = []
|
|
52
57
|
|
|
@@ -70,6 +75,7 @@ async function startEsbuildWatch(c: DankConfig): Promise<{ port: number }> {
|
|
|
70
75
|
})
|
|
71
76
|
})
|
|
72
77
|
html.rewriteHrefs()
|
|
78
|
+
html.appendScript(clientJS)
|
|
73
79
|
await html.writeTo(watchDir)
|
|
74
80
|
return html
|
|
75
81
|
}),
|
package/lib_js/esbuild.js
CHANGED
|
@@ -18,7 +18,7 @@ export async function esbuildDevContext(define, entryPoints, outdir) {
|
|
|
18
18
|
return await esbuild.context({
|
|
19
19
|
define,
|
|
20
20
|
entryNames: '[dir]/[name]',
|
|
21
|
-
entryPoints,
|
|
21
|
+
entryPoints: removeEntryPointOutExt(entryPoints),
|
|
22
22
|
outdir,
|
|
23
23
|
...webpageBuildOptions,
|
|
24
24
|
});
|
|
@@ -34,6 +34,9 @@ export async function esbuildWebpages(define, entryPoints, outdir) {
|
|
|
34
34
|
esbuildResultChecks(buildResult);
|
|
35
35
|
return buildResult.metafile;
|
|
36
36
|
}
|
|
37
|
+
// esbuild will append the .js or .css to output filenames
|
|
38
|
+
// keeping extension on entryPoints data for consistency
|
|
39
|
+
// and removing and mapping entryPoints to pass to esbuild
|
|
37
40
|
function removeEntryPointOutExt(entryPoints) {
|
|
38
41
|
return entryPoints.map(entryPoint => {
|
|
39
42
|
return {
|
package/lib_js/html.js
CHANGED
|
@@ -52,6 +52,12 @@ export class HtmlEntrypoint {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
appendScript(clientJS) {
|
|
56
|
+
const scriptNode = parseFragment(`<script type="module">${clientJS}</script>`).childNodes[0];
|
|
57
|
+
const htmlNode = this.#document.childNodes.find(node => node.nodeName === 'html');
|
|
58
|
+
const headNode = htmlNode.childNodes.find(node => node.nodeName === 'head');
|
|
59
|
+
defaultTreeAdapter.appendChild(headNode || htmlNode, scriptNode);
|
|
60
|
+
}
|
|
55
61
|
async writeTo(buildDir) {
|
|
56
62
|
await writeFile(join(buildDir, this.#url, 'index.html'), serialize(this.#document));
|
|
57
63
|
}
|
package/lib_js/serve.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { mkdir, rm } from 'node:fs/promises';
|
|
2
|
-
import { join } from 'node:path';
|
|
1
|
+
import { mkdir, readFile, rm } from 'node:fs/promises';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
3
|
import { buildWebsite } from "./build.js";
|
|
4
4
|
import { createGlobalDefinitions } from "./define.js";
|
|
5
5
|
import { esbuildDevContext } from "./esbuild.js";
|
|
@@ -33,6 +33,7 @@ async function startEsbuildWatch(c) {
|
|
|
33
33
|
const watchDir = join('build', 'watch');
|
|
34
34
|
await mkdir(watchDir, { recursive: true });
|
|
35
35
|
await copyAssets(watchDir);
|
|
36
|
+
const clientJS = await readFile(resolve(import.meta.dirname, join('..', 'client', 'esbuild.js')), 'utf-8');
|
|
36
37
|
const entryPointUrls = new Set();
|
|
37
38
|
const entryPoints = [];
|
|
38
39
|
await Promise.all(Object.entries(c.pages).map(async ([url, srcPath]) => {
|
|
@@ -51,6 +52,7 @@ async function startEsbuildWatch(c) {
|
|
|
51
52
|
});
|
|
52
53
|
});
|
|
53
54
|
html.rewriteHrefs();
|
|
55
|
+
html.appendScript(clientJS);
|
|
54
56
|
await html.writeTo(watchDir);
|
|
55
57
|
return html;
|
|
56
58
|
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eighty4/dank",
|
|
3
|
-
"version": "0.0.1-
|
|
3
|
+
"version": "0.0.1-3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": "./lib_js/bin.js",
|
|
6
6
|
"exports": {
|
|
@@ -20,12 +20,13 @@
|
|
|
20
20
|
"typescript": "^5.9.2"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
|
+
"client/esbuild.js",
|
|
23
24
|
"lib/*.ts",
|
|
24
25
|
"lib_js/*.js",
|
|
25
26
|
"lib_types/*.d.ts"
|
|
26
27
|
],
|
|
27
28
|
"scripts": {
|
|
28
|
-
"build": "tsc && tsc -p tsconfig.exports.json",
|
|
29
|
+
"build": "tsc && tsc -p tsconfig.client.json && tsc -p tsconfig.exports.json",
|
|
29
30
|
"fmt": "prettier --write .",
|
|
30
31
|
"fmtcheck": "prettier --check .",
|
|
31
32
|
"typecheck": "tsc --noEmit"
|