@eighty4/dank 0.0.4-1 → 0.0.4-2

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_js/serve.js CHANGED
@@ -1,268 +1,250 @@
1
- import { mkdir, readFile, rm, watch as _watch, writeFile, } from 'node:fs/promises';
2
- import { extname, join, resolve } from 'node:path';
1
+ import { mkdir, readFile, rm, watch as _watch, writeFile } from "node:fs/promises";
2
+ import { extname, join, resolve } from "node:path";
3
3
  import { buildWebsite } from "./build.js";
4
4
  import { loadConfig } from "./config.js";
5
5
  import { createGlobalDefinitions } from "./define.js";
6
6
  import { esbuildDevContext } from "./esbuild.js";
7
7
  import { resolveServeFlags } from "./flags.js";
8
8
  import { HtmlEntrypoint } from "./html.js";
9
- import { createBuiltDistFilesFetcher, createDevServeFilesFetcher, startWebServer, } from "./http.js";
9
+ import { createBuiltDistFilesFetcher, createDevServeFilesFetcher, startWebServer } from "./http.js";
10
10
  import { WebsiteRegistry } from "./metadata.js";
11
11
  import { startDevServices, updateDevServices } from "./services.js";
12
- export async function serveWebsite(c) {
13
- const serve = resolveServeFlags(c);
14
- await rm(serve.dirs.buildRoot, { force: true, recursive: true });
15
- const abortController = new AbortController();
16
- process.once('exit', () => abortController.abort());
17
- if (serve.preview) {
18
- await startPreviewMode(c, serve, abortController.signal);
19
- }
20
- else {
21
- await startDevMode(c, serve, abortController.signal);
22
- }
23
- return new Promise(() => { });
12
+ async function serveWebsite(c) {
13
+ const serve = resolveServeFlags(c);
14
+ await rm(serve.dirs.buildRoot, { force: true, recursive: true });
15
+ const abortController = new AbortController();
16
+ process.once("exit", () => abortController.abort());
17
+ if (serve.preview) {
18
+ await startPreviewMode(c, serve, abortController.signal);
19
+ } else {
20
+ await startDevMode(c, serve, abortController.signal);
21
+ }
22
+ return new Promise(() => {
23
+ });
24
24
  }
25
25
  async function startPreviewMode(c, serve, signal) {
26
- const manifest = await buildWebsite(c, serve);
27
- const frontend = createBuiltDistFilesFetcher(serve.dirs.buildDist, manifest);
28
- const devServices = startDevServices(c, signal);
29
- startWebServer(serve, frontend, devServices.http, {
30
- urls: Object.keys(c.pages),
31
- urlRewrites: collectUrlRewrites(c),
32
- });
26
+ const manifest = await buildWebsite(c);
27
+ const frontend = createBuiltDistFilesFetcher(serve.dirs.buildDist, manifest);
28
+ const devServices = startDevServices(c, signal);
29
+ startWebServer(serve, frontend, devServices.http, {
30
+ urls: Object.keys(c.pages),
31
+ urlRewrites: collectUrlRewrites(c)
32
+ });
33
33
  }
34
34
  function collectUrlRewrites(c) {
35
- return Object.keys(c.pages)
36
- .sort()
37
- .map(url => {
38
- const mapping = c.pages[url];
39
- return typeof mapping !== 'object' || !mapping.pattern
40
- ? null
41
- : { url, pattern: mapping.pattern };
42
- })
43
- .filter(mapping => mapping !== null);
35
+ return Object.keys(c.pages).sort().map((url) => {
36
+ const mapping = c.pages[url];
37
+ return typeof mapping !== "object" || !mapping.pattern ? null : { url, pattern: mapping.pattern };
38
+ }).filter((mapping) => mapping !== null);
44
39
  }
45
- // todo changing partials triggers update on html pages
46
40
  async function startDevMode(c, serve, signal) {
47
- await mkdir(serve.dirs.buildWatch, { recursive: true });
48
- const registry = new WebsiteRegistry(serve);
49
- const clientJS = await loadClientJS(serve.esbuildPort);
50
- const pagesByUrlPath = {};
51
- const partialsByUrlPath = {};
52
- const entryPointsByUrlPath = {};
53
- let buildContext = null;
54
- registry.on('workers', resetBuildContext);
55
- watch('dank.config.ts', signal, async () => {
56
- let updated;
57
- try {
58
- updated = await loadConfig();
59
- }
60
- catch (ignore) {
61
- return;
41
+ await mkdir(serve.dirs.buildWatch, { recursive: true });
42
+ const registry = new WebsiteRegistry(serve);
43
+ const clientJS = await loadClientJS(serve.esbuildPort);
44
+ const pagesByUrlPath = {};
45
+ const partialsByUrlPath = {};
46
+ const entryPointsByUrlPath = {};
47
+ let buildContext = null;
48
+ registry.on("workers", () => {
49
+ resetBuildContext();
50
+ });
51
+ watch("dank.config.ts", signal, async () => {
52
+ let updated;
53
+ try {
54
+ updated = await loadConfig("serve");
55
+ } catch (ignore) {
56
+ return;
57
+ }
58
+ const prevPages = new Set(Object.keys(pagesByUrlPath));
59
+ await Promise.all(Object.entries(updated.pages).map(async ([urlPath, mapping]) => {
60
+ c.pages[urlPath] = mapping;
61
+ const srcPath = typeof mapping === "string" ? mapping : mapping.webpage;
62
+ if (!pagesByUrlPath[urlPath]) {
63
+ await addPage(urlPath, srcPath);
64
+ } else {
65
+ prevPages.delete(urlPath);
66
+ if (pagesByUrlPath[urlPath].fsPath !== srcPath) {
67
+ await updatePage(urlPath);
62
68
  }
63
- const prevPages = new Set(Object.keys(pagesByUrlPath));
64
- await Promise.all(Object.entries(updated.pages).map(async ([urlPath, mapping]) => {
65
- c.pages[urlPath] = mapping;
66
- const srcPath = typeof mapping === 'string' ? mapping : mapping.webpage;
67
- if (!pagesByUrlPath[urlPath]) {
68
- await addPage(urlPath, srcPath);
69
- }
70
- else {
71
- prevPages.delete(urlPath);
72
- if (pagesByUrlPath[urlPath].fsPath !== srcPath) {
73
- await updatePage(urlPath);
74
- }
75
- }
76
- }));
77
- for (const prevPage of Array.from(prevPages)) {
78
- delete c.pages[prevPage];
79
- deletePage(prevPage);
69
+ }
70
+ }));
71
+ for (const prevPage of Array.from(prevPages)) {
72
+ delete c.pages[prevPage];
73
+ deletePage(prevPage);
74
+ }
75
+ updateDevServices(updated);
76
+ });
77
+ watch(serve.dirs.pages, signal, (filename) => {
78
+ if (extname(filename) === ".html") {
79
+ for (const [urlPath, srcPath] of Object.entries(c.pages)) {
80
+ if (srcPath === filename) {
81
+ updatePage(urlPath);
80
82
  }
81
- updateDevServices(updated);
82
- });
83
- watch(serve.dirs.pages, signal, filename => {
84
- if (extname(filename) === '.html') {
85
- for (const [urlPath, srcPath] of Object.entries(c.pages)) {
86
- if (srcPath === filename) {
87
- updatePage(urlPath);
88
- }
89
- }
90
- for (const [urlPath, partials] of Object.entries(partialsByUrlPath)) {
91
- if (partials.includes(filename)) {
92
- updatePage(urlPath, filename);
93
- }
94
- }
83
+ }
84
+ for (const [urlPath, partials] of Object.entries(partialsByUrlPath)) {
85
+ if (partials.includes(filename)) {
86
+ updatePage(urlPath, filename);
95
87
  }
96
- });
97
- await Promise.all(Object.entries(c.pages).map(async ([urlPath, mapping]) => {
98
- const srcPath = typeof mapping === 'string' ? mapping : mapping.webpage;
99
- await addPage(urlPath, srcPath);
100
- return new Promise(res => pagesByUrlPath[urlPath].once('entrypoints', res));
101
- }));
102
- async function addPage(urlPath, srcPath) {
103
- await mkdir(join(serve.dirs.buildWatch, urlPath), { recursive: true });
104
- const htmlEntrypoint = (pagesByUrlPath[urlPath] = new HtmlEntrypoint(serve, registry.resolver, urlPath, srcPath, [{ type: 'script', js: clientJS }]));
105
- htmlEntrypoint.on('entrypoints', entrypoints => {
106
- const pathsIn = new Set(entrypoints.map(e => e.in));
107
- if (!entryPointsByUrlPath[urlPath] ||
108
- !matchingEntrypoints(entryPointsByUrlPath[urlPath].pathsIn, pathsIn)) {
109
- entryPointsByUrlPath[urlPath] = { entrypoints, pathsIn };
110
- resetBuildContext();
111
- }
112
- });
113
- htmlEntrypoint.on('partial', partial => {
114
- if (!partialsByUrlPath[urlPath]) {
115
- partialsByUrlPath[urlPath] = [];
116
- }
117
- partialsByUrlPath[urlPath].push(partial);
118
- });
119
- htmlEntrypoint.on('partials', partials => (partialsByUrlPath[urlPath] = partials));
120
- htmlEntrypoint.on('output', html => writeFile(join(serve.dirs.buildWatch, urlPath, 'index.html'), html));
88
+ }
121
89
  }
122
- function deletePage(urlPath) {
123
- pagesByUrlPath[urlPath].removeAllListeners();
124
- delete pagesByUrlPath[urlPath];
125
- delete entryPointsByUrlPath[urlPath];
90
+ });
91
+ await Promise.all(Object.entries(c.pages).map(async ([urlPath, mapping]) => {
92
+ const srcPath = typeof mapping === "string" ? mapping : mapping.webpage;
93
+ await addPage(urlPath, srcPath);
94
+ return new Promise((res) => pagesByUrlPath[urlPath].once("entrypoints", res));
95
+ }));
96
+ async function addPage(urlPath, srcPath) {
97
+ await mkdir(join(serve.dirs.buildWatch, urlPath), { recursive: true });
98
+ const htmlEntrypoint = pagesByUrlPath[urlPath] = new HtmlEntrypoint(serve, registry.resolver, urlPath, srcPath, [{ type: "script", js: clientJS }]);
99
+ htmlEntrypoint.on("entrypoints", (entrypoints) => {
100
+ const pathsIn = new Set(entrypoints.map((e) => e.in));
101
+ if (!entryPointsByUrlPath[urlPath] || !matchingEntrypoints(entryPointsByUrlPath[urlPath].pathsIn, pathsIn)) {
102
+ entryPointsByUrlPath[urlPath] = { entrypoints, pathsIn };
126
103
  resetBuildContext();
104
+ }
105
+ });
106
+ htmlEntrypoint.on("partial", (partial) => {
107
+ if (!partialsByUrlPath[urlPath]) {
108
+ partialsByUrlPath[urlPath] = [];
109
+ }
110
+ partialsByUrlPath[urlPath].push(partial);
111
+ });
112
+ htmlEntrypoint.on("partials", (partials) => {
113
+ partialsByUrlPath[urlPath] = partials;
114
+ });
115
+ htmlEntrypoint.on("output", (html) => {
116
+ const path = join(serve.dirs.buildWatch, urlPath, "index.html");
117
+ writeFile(path, html);
118
+ });
119
+ }
120
+ function deletePage(urlPath) {
121
+ pagesByUrlPath[urlPath].removeAllListeners();
122
+ delete pagesByUrlPath[urlPath];
123
+ delete entryPointsByUrlPath[urlPath];
124
+ resetBuildContext();
125
+ }
126
+ async function updatePage(urlPath, partial) {
127
+ pagesByUrlPath[urlPath].emit("change", partial);
128
+ }
129
+ function collectEntrypoints() {
130
+ const unique = /* @__PURE__ */ new Set();
131
+ const pageBundles = Object.values(entryPointsByUrlPath).flatMap((entrypointState) => entrypointState.entrypoints).filter((entryPoint) => {
132
+ if (unique.has(entryPoint.in)) {
133
+ return false;
134
+ } else {
135
+ unique.add(entryPoint.in);
136
+ return true;
137
+ }
138
+ });
139
+ const workerBundles = registry.workerEntryPoints();
140
+ if (workerBundles) {
141
+ return [...pageBundles, ...workerBundles];
142
+ } else {
143
+ return pageBundles;
127
144
  }
128
- async function updatePage(urlPath, partial) {
129
- pagesByUrlPath[urlPath].emit('change', partial);
145
+ }
146
+ function resetBuildContext() {
147
+ switch (buildContext) {
148
+ case "starting":
149
+ buildContext = "dirty";
150
+ return;
151
+ case "dirty":
152
+ case "disposing":
153
+ return;
130
154
  }
131
- function collectEntrypoints() {
132
- const unique = new Set();
133
- const pageBundles = Object.values(entryPointsByUrlPath)
134
- .flatMap(entrypointState => entrypointState.entrypoints)
135
- .filter(entryPoint => {
136
- if (unique.has(entryPoint.in)) {
137
- return false;
138
- }
139
- else {
140
- unique.add(entryPoint.in);
141
- return true;
142
- }
143
- });
144
- const workerBundles = registry.workerEntryPoints();
145
- if (workerBundles) {
146
- return [...pageBundles, ...workerBundles];
147
- }
148
- else {
149
- return pageBundles;
155
+ if (buildContext !== null) {
156
+ const disposing = buildContext.dispose();
157
+ buildContext = "disposing";
158
+ disposing.then(() => {
159
+ buildContext = null;
160
+ resetBuildContext();
161
+ });
162
+ } else {
163
+ buildContext = "starting";
164
+ startEsbuildWatch(c, registry, serve, collectEntrypoints()).then((ctx) => {
165
+ if (buildContext === "dirty") {
166
+ buildContext = "disposing";
167
+ ctx.dispose().then(() => {
168
+ buildContext = null;
169
+ resetBuildContext();
170
+ });
171
+ } else {
172
+ buildContext = ctx;
150
173
  }
174
+ });
151
175
  }
152
- function resetBuildContext() {
153
- switch (buildContext) {
154
- case 'starting':
155
- buildContext = 'dirty';
156
- return;
157
- case 'dirty':
158
- case 'disposing':
159
- return;
160
- }
161
- if (buildContext !== null) {
162
- const disposing = buildContext.dispose();
163
- buildContext = 'disposing';
164
- disposing.then(() => {
165
- buildContext = null;
166
- resetBuildContext();
167
- });
168
- }
169
- else {
170
- buildContext = 'starting';
171
- startEsbuildWatch(c, registry, serve, collectEntrypoints()).then(ctx => {
172
- if (buildContext === 'dirty') {
173
- buildContext = 'disposing';
174
- ctx.dispose().then(() => {
175
- buildContext = null;
176
- resetBuildContext();
177
- });
178
- }
179
- else {
180
- buildContext = ctx;
181
- }
182
- });
183
- }
176
+ }
177
+ resetBuildContext();
178
+ const pageRoutes = {
179
+ get urls() {
180
+ return Object.keys(c.pages);
181
+ },
182
+ get urlRewrites() {
183
+ return collectUrlRewrites(c);
184
184
  }
185
- // function removePartialFromPage(partial: string, urlPath: string) {
186
- // const deleteIndex = urlPathsByPartials[partial].indexOf(urlPath)
187
- // if (deleteIndex !== -1) {
188
- // if (urlPathsByPartials[partial].length === 1) {
189
- // delete urlPathsByPartials[partial]
190
- // } else {
191
- // urlPathsByPartials[partial].splice(deleteIndex, 1)
192
- // }
193
- // }
194
- // }
195
- // inital start of esbuild ctx
196
- resetBuildContext();
197
- // todo this page route state could be built on change and reused
198
- const pageRoutes = {
199
- get urls() {
200
- return Object.keys(c.pages);
201
- },
202
- get urlRewrites() {
203
- return collectUrlRewrites(c);
204
- },
205
- };
206
- const frontend = createDevServeFilesFetcher(pageRoutes, serve);
207
- const devServices = startDevServices(c, signal);
208
- startWebServer(serve, frontend, devServices.http, pageRoutes);
185
+ };
186
+ const frontend = createDevServeFilesFetcher(pageRoutes, serve);
187
+ const devServices = startDevServices(c, signal);
188
+ startWebServer(serve, frontend, devServices.http, pageRoutes);
209
189
  }
210
190
  function matchingEntrypoints(a, b) {
211
- if (a.size !== b.size) {
212
- return false;
213
- }
214
- for (const v in a) {
215
- if (!b.has(v)) {
216
- return false;
217
- }
191
+ if (a.size !== b.size) {
192
+ return false;
193
+ }
194
+ for (const v in a) {
195
+ if (!b.has(v)) {
196
+ return false;
218
197
  }
219
- return true;
198
+ }
199
+ return true;
220
200
  }
221
201
  async function startEsbuildWatch(c, registry, serve, entryPoints) {
222
- const ctx = await esbuildDevContext(serve, registry, createGlobalDefinitions(serve), entryPoints, c.esbuild);
223
- await ctx.watch();
224
- await ctx.serve({
225
- host: '127.0.0.1',
226
- port: serve.esbuildPort,
227
- cors: {
228
- origin: ['127.0.0.1', 'localhost'].map(hostname => `http://${hostname}:${serve.dankPort}`),
229
- },
230
- });
231
- return ctx;
202
+ const ctx = await esbuildDevContext(serve, registry, createGlobalDefinitions(serve), entryPoints, c.esbuild);
203
+ await ctx.watch();
204
+ await ctx.serve({
205
+ host: "127.0.0.1",
206
+ port: serve.esbuildPort,
207
+ cors: {
208
+ origin: ["127.0.0.1", "localhost"].map((hostname) => `http://${hostname}:${serve.dankPort}`)
209
+ }
210
+ });
211
+ return ctx;
232
212
  }
233
213
  async function loadClientJS(esbuildPort) {
234
- const clientJS = await readFile(resolve(import.meta.dirname, join('..', 'client', 'esbuild.js')), 'utf-8');
235
- return clientJS.replace('3995', `${esbuildPort}`);
214
+ const clientJS = await readFile(resolve(import.meta.dirname, join("..", "client", "esbuild.js")), "utf-8");
215
+ return clientJS.replace("3995", `${esbuildPort}`);
236
216
  }
237
217
  async function watch(p, signal, fire) {
238
- const delayFire = 90;
239
- const timeout = 100;
240
- let changes = {};
241
- try {
242
- for await (const { filename } of _watch(p, {
243
- recursive: true,
244
- signal,
245
- })) {
246
- if (filename) {
247
- if (!changes[filename]) {
248
- const now = Date.now();
249
- changes[filename] = now + delayFire;
250
- setTimeout(() => {
251
- const now = Date.now();
252
- for (const [filename, then] of Object.entries(changes)) {
253
- if (then <= now) {
254
- fire(filename);
255
- delete changes[filename];
256
- }
257
- }
258
- }, timeout);
259
- }
218
+ const delayFire = 90;
219
+ const timeout = 100;
220
+ let changes = {};
221
+ try {
222
+ for await (const { filename } of _watch(p, {
223
+ recursive: true,
224
+ signal
225
+ })) {
226
+ if (filename) {
227
+ if (!changes[filename]) {
228
+ const now = Date.now();
229
+ changes[filename] = now + delayFire;
230
+ setTimeout(() => {
231
+ const now2 = Date.now();
232
+ for (const [filename2, then] of Object.entries(changes)) {
233
+ if (then <= now2) {
234
+ fire(filename2);
235
+ delete changes[filename2];
236
+ }
260
237
  }
238
+ }, timeout);
261
239
  }
240
+ }
262
241
  }
263
- catch (e) {
264
- if (e.name !== 'AbortError') {
265
- throw e;
266
- }
242
+ } catch (e) {
243
+ if (e.name !== "AbortError") {
244
+ throw e;
267
245
  }
246
+ }
268
247
  }
248
+ export {
249
+ serveWebsite
250
+ };