@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_js/http.js
CHANGED
|
@@ -1,230 +1,211 @@
|
|
|
1
|
-
import { createReadStream } from
|
|
2
|
-
import { stat } from
|
|
3
|
-
import { createServer
|
|
4
|
-
import { extname, join } from
|
|
5
|
-
import { Readable } from
|
|
6
|
-
import mime from
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
frontendFetcher(url, headers, res, () => onNotFound(req, url, headers, httpServices, pageRoutes, serve, res));
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
createServer(serve.logHttp ? createLogWrapper(handler) : handler).listen(serve.dankPort);
|
|
20
|
-
console.log(serve.preview ? 'preview' : 'dev', `server is live at http://127.0.0.1:${serve.dankPort}`);
|
|
21
|
-
}
|
|
22
|
-
async function onNotFound(req, url, headers, httpServices, pageRoutes, serve, res) {
|
|
23
|
-
if (req.method === 'GET' && extname(url.pathname) === '') {
|
|
24
|
-
const urlRewrite = tryUrlRewrites(url, pageRoutes, serve);
|
|
25
|
-
if (urlRewrite) {
|
|
26
|
-
streamFile(urlRewrite, res);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
const fetchResponse = await tryHttpServices(req, url, headers, httpServices);
|
|
31
|
-
if (fetchResponse) {
|
|
32
|
-
sendFetchResponse(res, fetchResponse);
|
|
1
|
+
import { createReadStream } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { extname, join } from "node:path";
|
|
5
|
+
import { Readable } from "node:stream";
|
|
6
|
+
import mime from "mime";
|
|
7
|
+
function startWebServer(port, flags, dirs, urlRewriteProvider, frontendFetcher, httpServices) {
|
|
8
|
+
const serverAddress = "http://localhost:" + port;
|
|
9
|
+
const handler = (req, res) => {
|
|
10
|
+
if (!req.url || !req.method) {
|
|
11
|
+
res.end();
|
|
12
|
+
} else {
|
|
13
|
+
const url = new URL(serverAddress + req.url);
|
|
14
|
+
const headers = convertHeadersToFetch(req.headers);
|
|
15
|
+
frontendFetcher(url, headers, res, () => onNotFound(req, url, headers, httpServices, flags, dirs, urlRewriteProvider, res));
|
|
33
16
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
17
|
+
};
|
|
18
|
+
createServer(flags.logHttp ? createLogWrapper(handler) : handler).listen(port);
|
|
19
|
+
console.log(flags.preview ? "preview" : "dev", `server is live at http://127.0.0.1:${port}`);
|
|
20
|
+
}
|
|
21
|
+
async function onNotFound(req, url, headers, httpServices, flags, dirs, urlRewriteProvider, res) {
|
|
22
|
+
if (req.method === "GET" && extname(url.pathname) === "") {
|
|
23
|
+
const urlRewrite = tryUrlRewrites(flags, dirs, urlRewriteProvider.urlRewrites, url);
|
|
24
|
+
if (urlRewrite) {
|
|
25
|
+
streamFile(urlRewrite, res);
|
|
26
|
+
return;
|
|
37
27
|
}
|
|
28
|
+
}
|
|
29
|
+
const fetchResponse = await tryHttpServices(req, url, headers, httpServices);
|
|
30
|
+
if (fetchResponse) {
|
|
31
|
+
sendFetchResponse(res, fetchResponse);
|
|
32
|
+
} else {
|
|
33
|
+
res.writeHead(404);
|
|
34
|
+
res.end();
|
|
35
|
+
}
|
|
38
36
|
}
|
|
39
37
|
async function sendFetchResponse(res, fetchResponse) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
38
|
+
res.writeHead(fetchResponse.status, void 0, convertHeadersFromFetch(fetchResponse.headers));
|
|
39
|
+
if (fetchResponse.body) {
|
|
40
|
+
Readable.fromWeb(fetchResponse.body).pipe(res);
|
|
41
|
+
} else {
|
|
42
|
+
res.end();
|
|
43
|
+
}
|
|
47
44
|
}
|
|
48
|
-
function tryUrlRewrites(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
? join(serve.dirs.buildWatch, urlRewrite.url, 'index.html')
|
|
52
|
-
: null;
|
|
45
|
+
function tryUrlRewrites(flags, dirs, urlRewrites, url) {
|
|
46
|
+
const urlRewrite = urlRewrites.find((urlRewrite2) => urlRewrite2.pattern.test(url.pathname));
|
|
47
|
+
return urlRewrite ? join(flags.preview ? dirs.buildDist : dirs.buildWatch, urlRewrite.url, "index.html") : null;
|
|
53
48
|
}
|
|
54
49
|
async function tryHttpServices(req, url, headers, httpServices) {
|
|
55
|
-
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
const body = await collectReqBody(req);
|
|
59
|
-
const { running } = httpServices;
|
|
60
|
-
for (const httpService of running) {
|
|
61
|
-
const proxyUrl = new URL(url);
|
|
62
|
-
proxyUrl.port = `${httpService.port}`;
|
|
63
|
-
try {
|
|
64
|
-
const response = await retryFetchWithTimeout(proxyUrl, {
|
|
65
|
-
body,
|
|
66
|
-
headers,
|
|
67
|
-
method: req.method,
|
|
68
|
-
redirect: 'manual',
|
|
69
|
-
});
|
|
70
|
-
if (response.status === 404 || response.status === 405) {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
return response;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
catch (e) {
|
|
78
|
-
if (e === 'retrytimeout') {
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
errorExit(`unexpected error http proxying to port ${httpService.port}: ${e.message}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
50
|
+
if (url.pathname.startsWith("/.well-known/")) {
|
|
86
51
|
return null;
|
|
52
|
+
}
|
|
53
|
+
const body = await collectReqBody(req);
|
|
54
|
+
const { running } = httpServices;
|
|
55
|
+
for (const httpService of running) {
|
|
56
|
+
const proxyUrl = new URL(url);
|
|
57
|
+
proxyUrl.port = `${httpService.port}`;
|
|
58
|
+
try {
|
|
59
|
+
const response = await retryFetchWithTimeout(proxyUrl, {
|
|
60
|
+
body,
|
|
61
|
+
headers,
|
|
62
|
+
method: req.method,
|
|
63
|
+
redirect: "manual"
|
|
64
|
+
});
|
|
65
|
+
if (response.status === 404 || response.status === 405) {
|
|
66
|
+
continue;
|
|
67
|
+
} else {
|
|
68
|
+
return response;
|
|
69
|
+
}
|
|
70
|
+
} catch (e) {
|
|
71
|
+
if (e === "retrytimeout") {
|
|
72
|
+
continue;
|
|
73
|
+
} else {
|
|
74
|
+
errorExit(`unexpected error http proxying to port ${httpService.port}: ${e.message}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
87
79
|
}
|
|
88
80
|
function collectReqBody(req) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
let body = "";
|
|
82
|
+
req.on("data", (data) => body += data.toString());
|
|
83
|
+
return new Promise((res) => req.on("end", () => res(body.length ? body : null)));
|
|
92
84
|
}
|
|
93
85
|
function createLogWrapper(handler) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
})
|
|
141
|
-
.catch(e => {
|
|
142
|
-
if (isFetchRetryTimeout(e)) {
|
|
143
|
-
res.writeHead(504);
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
console.error('unknown frontend proxy fetch error:', e);
|
|
147
|
-
res.writeHead(502);
|
|
148
|
-
}
|
|
149
|
-
res.end();
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
});
|
|
86
|
+
return (req, res) => {
|
|
87
|
+
console.log(" > ", req.method, req.url);
|
|
88
|
+
res.on("close", () => {
|
|
89
|
+
console.log("", res.statusCode, req.method, req.url);
|
|
90
|
+
});
|
|
91
|
+
handler(req, res);
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function createBuiltDistFilesFetcher(dirs, manifest) {
|
|
95
|
+
return (url, _headers, res, notFound) => {
|
|
96
|
+
if (manifest.pageUrls.has(url.pathname)) {
|
|
97
|
+
streamFile(join(dirs.projectResolved, dirs.buildDist, url.pathname, "index.html"), res);
|
|
98
|
+
} else if (manifest.files.has(url.pathname)) {
|
|
99
|
+
streamFile(join(dirs.projectResolved, dirs.buildDist, url.pathname), res);
|
|
100
|
+
} else {
|
|
101
|
+
notFound();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function createDevServeFilesFetcher(esbuildPort, dirs, registry) {
|
|
106
|
+
const proxyAddress = "http://127.0.0.1:" + esbuildPort;
|
|
107
|
+
return (url, _headers, res, notFound) => {
|
|
108
|
+
if (registry.pageUrls.includes(url.pathname)) {
|
|
109
|
+
streamFile(join(dirs.buildWatch, url.pathname, "index.html"), res);
|
|
110
|
+
} else {
|
|
111
|
+
const maybePublicPath = join(dirs.public, url.pathname);
|
|
112
|
+
exists(maybePublicPath).then((fromPublic) => {
|
|
113
|
+
if (fromPublic) {
|
|
114
|
+
streamFile(maybePublicPath, res);
|
|
115
|
+
} else {
|
|
116
|
+
retryFetchWithTimeout(proxyAddress + url.pathname).then((fetchResponse) => {
|
|
117
|
+
if (fetchResponse.status === 404) {
|
|
118
|
+
notFound();
|
|
119
|
+
} else {
|
|
120
|
+
res.writeHead(fetchResponse.status, convertHeadersFromFetch(fetchResponse.headers));
|
|
121
|
+
fetchResponse.bytes().then((data) => res.end(data));
|
|
122
|
+
}
|
|
123
|
+
}).catch((e) => {
|
|
124
|
+
if (isFetchRetryTimeout(e)) {
|
|
125
|
+
res.writeHead(504);
|
|
126
|
+
} else {
|
|
127
|
+
console.error("unknown frontend proxy fetch error:", e);
|
|
128
|
+
res.writeHead(502);
|
|
129
|
+
}
|
|
130
|
+
res.end();
|
|
131
|
+
});
|
|
153
132
|
}
|
|
154
|
-
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
};
|
|
155
136
|
}
|
|
156
137
|
const PROXY_FETCH_RETRY_INTERVAL = 27;
|
|
157
|
-
const PROXY_FETCH_RETRY_TIMEOUT =
|
|
138
|
+
const PROXY_FETCH_RETRY_TIMEOUT = 1e3;
|
|
158
139
|
async function retryFetchWithTimeout(url, requestInit) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
else {
|
|
170
|
-
await new Promise(res => setTimeout(res, PROXY_FETCH_RETRY_INTERVAL));
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
throw e;
|
|
175
|
-
}
|
|
140
|
+
let timeout = Date.now() + PROXY_FETCH_RETRY_TIMEOUT;
|
|
141
|
+
while (true) {
|
|
142
|
+
try {
|
|
143
|
+
return await fetch(url, requestInit);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
if (isNodeFailedFetch(e) || isBunFailedFetch(e)) {
|
|
146
|
+
if (timeout < Date.now()) {
|
|
147
|
+
throw "retrytimeout";
|
|
148
|
+
} else {
|
|
149
|
+
await new Promise((res) => setTimeout(res, PROXY_FETCH_RETRY_INTERVAL));
|
|
176
150
|
}
|
|
151
|
+
} else {
|
|
152
|
+
throw e;
|
|
153
|
+
}
|
|
177
154
|
}
|
|
155
|
+
}
|
|
178
156
|
}
|
|
179
157
|
function isFetchRetryTimeout(e) {
|
|
180
|
-
|
|
158
|
+
return e === "retrytimeout";
|
|
181
159
|
}
|
|
182
160
|
function isBunFailedFetch(e) {
|
|
183
|
-
|
|
161
|
+
return e.code === "ConnectionRefused";
|
|
184
162
|
}
|
|
185
163
|
function isNodeFailedFetch(e) {
|
|
186
|
-
|
|
164
|
+
return e.message === "fetch failed";
|
|
187
165
|
}
|
|
188
166
|
async function exists(p) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
167
|
+
try {
|
|
168
|
+
const maybe = stat(p);
|
|
169
|
+
return (await maybe).isFile();
|
|
170
|
+
} catch (ignore) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
196
173
|
}
|
|
197
174
|
function streamFile(p, res) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
175
|
+
res.setHeader("Content-Type", mime.getType(p) || "application/octet-stream");
|
|
176
|
+
const reading = createReadStream(p);
|
|
177
|
+
reading.pipe(res);
|
|
178
|
+
reading.on("error", (err) => {
|
|
179
|
+
console.error(`file read ${reading.path} error ${err.message}`);
|
|
180
|
+
res.statusCode = 500;
|
|
181
|
+
res.end();
|
|
182
|
+
});
|
|
206
183
|
}
|
|
207
184
|
function convertHeadersFromFetch(from) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
185
|
+
const to = {};
|
|
186
|
+
for (const name of from.keys()) {
|
|
187
|
+
to[name] = from.get(name);
|
|
188
|
+
}
|
|
189
|
+
return to;
|
|
213
190
|
}
|
|
214
191
|
function convertHeadersToFetch(from) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
to.set(name, values);
|
|
223
|
-
}
|
|
192
|
+
const to = new Headers();
|
|
193
|
+
for (const [name, values] of Object.entries(from)) {
|
|
194
|
+
if (Array.isArray(values)) {
|
|
195
|
+
for (const value of values)
|
|
196
|
+
to.append(name, value);
|
|
197
|
+
} else if (values) {
|
|
198
|
+
to.set(name, values);
|
|
224
199
|
}
|
|
225
|
-
|
|
200
|
+
}
|
|
201
|
+
return to;
|
|
226
202
|
}
|
|
227
203
|
function errorExit(msg) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
204
|
+
console.log(`\x1B[31merror:\x1B[0m`, msg);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
export {
|
|
208
|
+
createBuiltDistFilesFetcher,
|
|
209
|
+
createDevServeFilesFetcher,
|
|
210
|
+
startWebServer
|
|
211
|
+
};
|
package/lib_js/public.js
CHANGED
|
@@ -1,51 +1,50 @@
|
|
|
1
|
-
import { copyFile, mkdir, readdir, stat } from
|
|
2
|
-
import { platform } from
|
|
3
|
-
import { join } from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
throw Error('./public cannot be a file');
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
catch (e) {
|
|
16
|
-
return null;
|
|
1
|
+
import { copyFile, mkdir, readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { platform } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
async function copyAssets(dirs) {
|
|
5
|
+
try {
|
|
6
|
+
const stats = await stat(dirs.public);
|
|
7
|
+
if (stats.isDirectory()) {
|
|
8
|
+
await mkdir(dirs.buildDist, { recursive: true });
|
|
9
|
+
return await recursiveCopyAssets(dirs);
|
|
10
|
+
} else {
|
|
11
|
+
throw Error("./public cannot be a file");
|
|
17
12
|
}
|
|
13
|
+
} catch (e) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
18
16
|
}
|
|
19
|
-
const IGNORE = platform() ===
|
|
20
|
-
async function recursiveCopyAssets(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
madeDir = true;
|
|
40
|
-
}
|
|
41
|
-
await copyFile(join(listingDir, p), join(to, p));
|
|
42
|
-
copied.push('/' + join(dir, p).replaceAll('\\', '/'));
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
catch (e) {
|
|
46
|
-
console.error('stat error', e);
|
|
47
|
-
process.exit(1);
|
|
17
|
+
const IGNORE = platform() === "darwin" ? [".DS_Store"] : [];
|
|
18
|
+
async function recursiveCopyAssets(dirs, dir = "") {
|
|
19
|
+
const copied = [];
|
|
20
|
+
const to = join(dirs.buildDist, dir);
|
|
21
|
+
let madeDir = dir === "";
|
|
22
|
+
const listingDir = join(dirs.public, dir);
|
|
23
|
+
for (const p of await readdir(listingDir)) {
|
|
24
|
+
if (IGNORE.includes(p)) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const stats = await stat(join(listingDir, p));
|
|
29
|
+
if (stats.isDirectory()) {
|
|
30
|
+
copied.push(...await recursiveCopyAssets(dirs, join(dir, p)));
|
|
31
|
+
} else {
|
|
32
|
+
if (!madeDir) {
|
|
33
|
+
await mkdir(join(dirs.buildDist, dir), {
|
|
34
|
+
recursive: true
|
|
35
|
+
});
|
|
36
|
+
madeDir = true;
|
|
48
37
|
}
|
|
38
|
+
await copyFile(join(listingDir, p), join(to, p));
|
|
39
|
+
copied.push("/" + join(dir, p).replaceAll("\\", "/"));
|
|
40
|
+
}
|
|
41
|
+
} catch (e) {
|
|
42
|
+
console.error("stat error", e);
|
|
43
|
+
process.exit(1);
|
|
49
44
|
}
|
|
50
|
-
|
|
45
|
+
}
|
|
46
|
+
return copied;
|
|
51
47
|
}
|
|
48
|
+
export {
|
|
49
|
+
copyAssets
|
|
50
|
+
};
|