@eighty4/dank 0.0.4-0 → 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/serve.ts CHANGED
@@ -11,6 +11,7 @@ import { buildWebsite } from './build.ts'
11
11
  import { loadConfig } from './config.ts'
12
12
  import type { DankConfig } from './dank.ts'
13
13
  import { createGlobalDefinitions } from './define.ts'
14
+ import { LOG } from './developer.ts'
14
15
  import { esbuildDevContext, type EntryPoint } from './esbuild.ts'
15
16
  import { resolveServeFlags, type DankServe } from './flags.ts'
16
17
  import { HtmlEntrypoint } from './html.ts'
@@ -42,7 +43,7 @@ async function startPreviewMode(
42
43
  serve: DankServe,
43
44
  signal: AbortSignal,
44
45
  ) {
45
- const manifest = await buildWebsite(c, serve)
46
+ const manifest = await buildWebsite(c)
46
47
  const frontend = createBuiltDistFilesFetcher(serve.dirs.buildDist, manifest)
47
48
  const devServices = startDevServices(c, signal)
48
49
  startWebServer(serve, frontend, devServices.http, {
@@ -89,12 +90,21 @@ async function startDevMode(
89
90
  const entryPointsByUrlPath: Record<string, EntrypointsState> = {}
90
91
  let buildContext: BuildContextState = null
91
92
 
92
- registry.on('workers', resetBuildContext)
93
+ registry.on('workers', () => {
94
+ LOG({
95
+ realm: 'serve',
96
+ message: 'registry updated worker entrypoints',
97
+ data: {
98
+ workers: registry.workerEntryPoints()?.map(ep => ep.in) || null,
99
+ },
100
+ })
101
+ resetBuildContext()
102
+ })
93
103
 
94
104
  watch('dank.config.ts', signal, async () => {
95
105
  let updated: DankConfig
96
106
  try {
97
- updated = await loadConfig()
107
+ updated = await loadConfig('serve')
98
108
  } catch (ignore) {
99
109
  return
100
110
  }
@@ -105,16 +115,41 @@ async function startDevMode(
105
115
  const srcPath =
106
116
  typeof mapping === 'string' ? mapping : mapping.webpage
107
117
  if (!pagesByUrlPath[urlPath]) {
118
+ LOG({
119
+ realm: 'config',
120
+ message: 'added page',
121
+ data: {
122
+ urlPath,
123
+ srcPath,
124
+ },
125
+ })
108
126
  await addPage(urlPath, srcPath)
109
127
  } else {
110
128
  prevPages.delete(urlPath)
111
129
  if (pagesByUrlPath[urlPath].fsPath !== srcPath) {
130
+ LOG({
131
+ realm: 'config',
132
+ message: 'updated page src',
133
+ data: {
134
+ urlPath,
135
+ newSrcPath: srcPath,
136
+ oldSrcPath: pagesByUrlPath[urlPath].fsPath,
137
+ },
138
+ })
112
139
  await updatePage(urlPath)
113
140
  }
114
141
  }
115
142
  }),
116
143
  )
117
144
  for (const prevPage of Array.from(prevPages)) {
145
+ LOG({
146
+ realm: 'config',
147
+ message: 'removed page',
148
+ data: {
149
+ urlPath: prevPage,
150
+ srcPath: c.pages[prevPage as `/${string}`] as string,
151
+ },
152
+ })
118
153
  delete c.pages[prevPage as `/${string}`]
119
154
  deletePage(prevPage)
120
155
  }
@@ -122,6 +157,13 @@ async function startDevMode(
122
157
  })
123
158
 
124
159
  watch(serve.dirs.pages, signal, filename => {
160
+ LOG({
161
+ realm: 'serve',
162
+ message: 'pages dir watch event',
163
+ data: {
164
+ file: filename,
165
+ },
166
+ })
125
167
  if (extname(filename) === '.html') {
126
168
  for (const [urlPath, srcPath] of Object.entries(c.pages)) {
127
169
  if (srcPath === filename) {
@@ -153,6 +195,7 @@ async function startDevMode(
153
195
  await mkdir(join(serve.dirs.buildWatch, urlPath), { recursive: true })
154
196
  const htmlEntrypoint = (pagesByUrlPath[urlPath] = new HtmlEntrypoint(
155
197
  serve,
198
+ registry.resolver,
156
199
  urlPath,
157
200
  srcPath,
158
201
  [{ type: 'script', js: clientJS }],
@@ -166,23 +209,56 @@ async function startDevMode(
166
209
  pathsIn,
167
210
  )
168
211
  ) {
212
+ LOG({
213
+ realm: 'serve',
214
+ message: 'html entrypoints event',
215
+ data: {
216
+ previous:
217
+ entryPointsByUrlPath[urlPath]?.pathsIn || null,
218
+ new: pathsIn,
219
+ },
220
+ })
169
221
  entryPointsByUrlPath[urlPath] = { entrypoints, pathsIn }
170
222
  resetBuildContext()
171
223
  }
172
224
  })
173
225
  htmlEntrypoint.on('partial', partial => {
226
+ LOG({
227
+ realm: 'serve',
228
+ message: 'html partial event',
229
+ data: {
230
+ webpage: htmlEntrypoint.fsPath,
231
+ partial,
232
+ },
233
+ })
174
234
  if (!partialsByUrlPath[urlPath]) {
175
235
  partialsByUrlPath[urlPath] = []
176
236
  }
177
237
  partialsByUrlPath[urlPath].push(partial)
178
238
  })
179
- htmlEntrypoint.on(
180
- 'partials',
181
- partials => (partialsByUrlPath[urlPath] = partials),
182
- )
183
- htmlEntrypoint.on('output', html =>
184
- writeFile(join(serve.dirs.buildWatch, urlPath, 'index.html'), html),
185
- )
239
+ htmlEntrypoint.on('partials', partials => {
240
+ LOG({
241
+ realm: 'serve',
242
+ message: 'html partials event',
243
+ data: {
244
+ allPartials: partials.length === 0,
245
+ partials,
246
+ },
247
+ })
248
+ partialsByUrlPath[urlPath] = partials
249
+ })
250
+ htmlEntrypoint.on('output', html => {
251
+ const path = join(serve.dirs.buildWatch, urlPath, 'index.html')
252
+ LOG({
253
+ realm: 'serve',
254
+ message: 'html output event',
255
+ data: {
256
+ webpage: htmlEntrypoint.fsPath,
257
+ path,
258
+ },
259
+ })
260
+ writeFile(path, html)
261
+ })
186
262
  }
187
263
 
188
264
  function deletePage(urlPath: string) {
@@ -226,6 +302,7 @@ async function startDevMode(
226
302
  return
227
303
  }
228
304
  if (buildContext !== null) {
305
+ LOG({ realm: 'serve', message: 'disposing esbuild context' })
229
306
  const disposing = buildContext.dispose()
230
307
  buildContext = 'disposing'
231
308
  disposing.then(() => {
@@ -296,6 +373,13 @@ async function startEsbuildWatch(
296
373
  serve: DankServe,
297
374
  entryPoints: Array<EntryPoint>,
298
375
  ): Promise<BuildContext> {
376
+ LOG({
377
+ realm: 'serve',
378
+ message: 'starting esbuild watch',
379
+ data: {
380
+ entrypoints: entryPoints.map(ep => ep.in),
381
+ },
382
+ })
299
383
  const ctx = await esbuildDevContext(
300
384
  serve,
301
385
  registry,
package/lib_js/bin.js CHANGED
@@ -2,105 +2,101 @@
2
2
  import { buildWebsite } from "./build.js";
3
3
  import { loadConfig } from "./config.js";
4
4
  import { serveWebsite } from "./serve.js";
5
- function printHelp(task) {
6
- if (!task || task === 'build') {
7
- console.log('dank build [--minify] [--production]');
8
- }
9
- if (!task || task === 'serve') {
10
- console.log(
11
- // 'dank serve [--minify] [--preview] [--production]',
12
- 'dank serve [--minify] [--production]');
13
- }
14
- console.log('\nOPTIONS:');
15
- if (!task || task === 'serve')
16
- console.log(' --log-http print access logs');
17
- console.log(' --minify minify sources');
18
- // if (!task || task === 'serve') console.log(' --preview pre-bundle and build ServiceWorker')
19
- console.log(' --production build for production release');
20
- if (task) {
21
- console.log();
22
- console.log('use `dank -h` for details on all commands');
23
- }
24
- process.exit(1);
5
+ function printHelp(task2) {
6
+ if (!task2 || task2 === "build") {
7
+ console.log("dank build [--minify] [--production]");
8
+ }
9
+ if (!task2 || task2 === "serve") {
10
+ console.log(
11
+ // 'dank serve [--minify] [--preview] [--production]',
12
+ "dank serve [--minify] [--production]"
13
+ );
14
+ }
15
+ console.log("\nOPTIONS:");
16
+ if (!task2 || task2 === "serve")
17
+ console.log(" --log-http print access logs");
18
+ console.log(" --minify minify sources");
19
+ console.log(" --production build for production release");
20
+ if (task2) {
21
+ console.log();
22
+ console.log("use `dank -h` for details on all commands");
23
+ }
24
+ process.exit(1);
25
25
  }
26
26
  const args = (function collectProgramArgs() {
27
- const programNames = ['dank', 'bin.js', 'bin.ts'];
28
- let args = [...process.argv];
29
- while (true) {
30
- const shifted = args.shift();
31
- if (!shifted || programNames.some(name => shifted.endsWith(name))) {
32
- return args;
33
- }
27
+ const programNames = ["dank", "bin.js", "bin.ts"];
28
+ let args2 = [...process.argv];
29
+ while (true) {
30
+ const shifted = args2.shift();
31
+ if (!shifted || programNames.some((name) => shifted.endsWith(name))) {
32
+ return args2;
34
33
  }
34
+ }
35
35
  })();
36
36
  const task = (function resolveTask() {
37
- const showHelp = args.some(arg => arg === '-h' || arg === '--help');
38
- const task = (() => {
39
- while (true) {
40
- const shifted = args.shift();
41
- switch (shifted) {
42
- case '-h':
43
- case '--help':
44
- break;
45
- case 'build':
46
- return 'build';
47
- case 'dev':
48
- case 'serve':
49
- return 'serve';
50
- default:
51
- if (showHelp) {
52
- printHelp();
53
- }
54
- else if (typeof shifted === 'undefined') {
55
- printError('missing command');
56
- printHelp();
57
- }
58
- else {
59
- printError(shifted + " isn't a command");
60
- printHelp();
61
- }
62
- }
63
- }
64
- })();
65
- if (showHelp) {
66
- printHelp(task);
37
+ const showHelp = args.some((arg) => arg === "-h" || arg === "--help");
38
+ const task2 = (() => {
39
+ while (true) {
40
+ const shifted = args.shift();
41
+ switch (shifted) {
42
+ case "-h":
43
+ case "--help":
44
+ break;
45
+ case "build":
46
+ return "build";
47
+ case "dev":
48
+ case "serve":
49
+ return "serve";
50
+ default:
51
+ if (showHelp) {
52
+ printHelp();
53
+ } else if (typeof shifted === "undefined") {
54
+ printError("missing command");
55
+ printHelp();
56
+ } else {
57
+ printError(shifted + " isn't a command");
58
+ printHelp();
59
+ }
60
+ }
67
61
  }
68
- return task;
62
+ })();
63
+ if (showHelp) {
64
+ printHelp(task2);
65
+ }
66
+ return task2;
69
67
  })();
70
- const c = await loadConfig();
68
+ const c = await loadConfig(task);
71
69
  try {
72
- switch (task) {
73
- case 'build':
74
- await buildWebsite(c);
75
- console.log(green('done'));
76
- process.exit(0);
77
- case 'serve':
78
- await serveWebsite(c);
79
- }
80
- }
81
- catch (e) {
82
- errorExit(e);
70
+ switch (task) {
71
+ case "build":
72
+ await buildWebsite(c);
73
+ console.log(green("done"));
74
+ process.exit(0);
75
+ case "serve":
76
+ await serveWebsite(c);
77
+ }
78
+ } catch (e) {
79
+ errorExit(e);
83
80
  }
84
81
  function printError(e) {
85
- if (e !== null) {
86
- if (typeof e === 'string') {
87
- console.error(red('error:'), e);
88
- }
89
- else if (e instanceof Error) {
90
- console.error(red('error:'), e.message);
91
- if (e.stack) {
92
- console.error(e.stack);
93
- }
94
- }
82
+ if (e !== null) {
83
+ if (typeof e === "string") {
84
+ console.error(red("error:"), e);
85
+ } else if (e instanceof Error) {
86
+ console.error(red("error:"), e.message);
87
+ if (e.stack) {
88
+ console.error(e.stack);
89
+ }
95
90
  }
91
+ }
96
92
  }
97
93
  function green(s) {
98
- return `\u001b[32m${s}\u001b[0m`;
94
+ return `\x1B[32m${s}\x1B[0m`;
99
95
  }
100
96
  function red(s) {
101
- return `\u001b[31m${s}\u001b[0m`;
97
+ return `\x1B[31m${s}\x1B[0m`;
102
98
  }
103
99
  function errorExit(e) {
104
- printError(e);
105
- process.exit(1);
100
+ printError(e);
101
+ process.exit(1);
106
102
  }
package/lib_js/build.js CHANGED
@@ -1,5 +1,5 @@
1
- import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
2
- import { join } from 'node:path';
1
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
3
  import { createBuildTag } from "./build_tag.js";
4
4
  import { createGlobalDefinitions } from "./define.js";
5
5
  import { esbuildWebpages, esbuildWorkers } from "./esbuild.js";
@@ -7,89 +7,80 @@ import { resolveBuildFlags } from "./flags.js";
7
7
  import { HtmlEntrypoint } from "./html.js";
8
8
  import { WebsiteRegistry } from "./metadata.js";
9
9
  import { copyAssets } from "./public.js";
10
- export async function buildWebsite(c, build = resolveBuildFlags()) {
11
- const buildTag = await createBuildTag(build);
12
- console.log(build.minify
13
- ? build.production
14
- ? 'minified production'
15
- : 'minified'
16
- : 'unminified', 'build', buildTag, 'building in ./build/dist');
17
- await rm(build.dirs.buildRoot, { recursive: true, force: true });
18
- await mkdir(build.dirs.buildDist, { recursive: true });
19
- for (const subdir of Object.keys(c.pages).filter(url => url !== '/')) {
20
- await mkdir(join(build.dirs.buildDist, subdir), { recursive: true });
21
- }
22
- await mkdir(join(build.dirs.buildRoot, 'metafiles'), { recursive: true });
23
- const registry = new WebsiteRegistry(build);
24
- registry.pageUrls = Object.keys(c.pages);
25
- registry.copiedAssets = await copyAssets(build);
26
- await buildWebpages(c, registry, build, createGlobalDefinitions(build));
27
- return await registry.writeManifest(buildTag);
10
+ async function buildWebsite(c) {
11
+ const build = resolveBuildFlags();
12
+ const buildTag = await createBuildTag(build);
13
+ console.log(build.minify ? build.production ? "minified production" : "minified" : "unminified", "build", buildTag, "building in ./build/dist");
14
+ await rm(build.dirs.buildRoot, { recursive: true, force: true });
15
+ await mkdir(build.dirs.buildDist, { recursive: true });
16
+ for (const subdir of Object.keys(c.pages).filter((url) => url !== "/")) {
17
+ await mkdir(join(build.dirs.buildDist, subdir), { recursive: true });
18
+ }
19
+ await mkdir(join(build.dirs.buildRoot, "metafiles"), { recursive: true });
20
+ const registry = new WebsiteRegistry(build);
21
+ registry.pageUrls = Object.keys(c.pages);
22
+ registry.copiedAssets = await copyAssets(build);
23
+ await buildWebpages(c, registry, build, createGlobalDefinitions(build));
24
+ return await registry.writeManifest(buildTag);
28
25
  }
29
- // builds all webpage entrypoints in one esbuild.build context
30
- // to support code splitting
31
- // returns all built assets URLs and webpage URLs from DankConfig.pages
32
26
  async function buildWebpages(c, registry, build, define) {
33
- // create HtmlEntrypoint for each webpage and collect awaitable esbuild entrypoints
34
- const loadingEntryPoints = [];
35
- const htmlEntrypoints = [];
36
- for (const [urlPath, mapping] of Object.entries(c.pages)) {
37
- const fsPath = typeof mapping === 'string' ? mapping : mapping.webpage;
38
- const html = new HtmlEntrypoint(build, urlPath, fsPath);
39
- loadingEntryPoints.push(new Promise(res => html.on('entrypoints', res)));
40
- htmlEntrypoints.push(html);
41
- }
42
- // collect esbuild entrypoints from every HtmlEntrypoint
43
- const uniqueEntryPoints = new Set();
44
- const buildEntryPoints = [];
45
- for (const pageEntryPoints of await Promise.all(loadingEntryPoints)) {
46
- for (const entryPoint of pageEntryPoints) {
47
- if (!uniqueEntryPoints.has(entryPoint.in)) {
48
- buildEntryPoints.push(entryPoint);
49
- }
50
- }
27
+ const loadingEntryPoints = [];
28
+ const htmlEntrypoints = [];
29
+ for (const [urlPath, mapping] of Object.entries(c.pages)) {
30
+ const fsPath = typeof mapping === "string" ? mapping : mapping.webpage;
31
+ const html = new HtmlEntrypoint(build, registry.resolver, urlPath, fsPath);
32
+ loadingEntryPoints.push(new Promise((res) => html.on("entrypoints", res)));
33
+ htmlEntrypoints.push(html);
34
+ }
35
+ const uniqueEntryPoints = /* @__PURE__ */ new Set();
36
+ const buildEntryPoints = [];
37
+ for (const pageEntryPoints of await Promise.all(loadingEntryPoints)) {
38
+ for (const entryPoint of pageEntryPoints) {
39
+ if (!uniqueEntryPoints.has(entryPoint.in)) {
40
+ buildEntryPoints.push(entryPoint);
41
+ }
51
42
  }
52
- await esbuildWebpages(build, registry, define, buildEntryPoints, c.esbuild);
53
- // todo recursively build workers on building workers that create workers
54
- const workerEntryPoints = registry.workerEntryPoints();
55
- if (workerEntryPoints?.length) {
56
- await esbuildWorkers(build, registry, define, workerEntryPoints, c.esbuild);
57
- }
58
- await rewriteWorkerUrls(build, registry);
59
- // write out html output with rewritten hrefs
60
- await Promise.all(htmlEntrypoints.map(async (html) => {
61
- await writeFile(join(build.dirs.buildDist, html.url, 'index.html'), html.output(registry));
62
- }));
43
+ }
44
+ await esbuildWebpages(build, registry, define, buildEntryPoints, c.esbuild);
45
+ const workerEntryPoints = registry.workerEntryPoints();
46
+ if (workerEntryPoints?.length) {
47
+ await esbuildWorkers(build, registry, define, workerEntryPoints, c.esbuild);
48
+ }
49
+ await rewriteWorkerUrls(build, registry);
50
+ await Promise.all(htmlEntrypoints.map(async (html) => {
51
+ await writeFile(join(build.dirs.buildDist, html.url, "index.html"), html.output(registry));
52
+ }));
63
53
  }
64
- export async function rewriteWorkerUrls(build, registry) {
65
- const workers = registry.workers();
66
- if (!workers) {
67
- return;
68
- }
69
- const dependentBundlePaths = workers.map(w => registry.mappedHref(w.dependentEntryPoint));
70
- const bundleOutputs = {};
71
- // collect all js file contents concurrently
72
- const readingFiles = Promise.all(dependentBundlePaths.map(async (p) => {
73
- bundleOutputs[p] = await readFile(join(build.dirs.projectRootAbs, build.dirs.buildDist, p), 'utf8');
74
- }));
75
- // build regex replacements during file reads
76
- const rewriteChains = {};
77
- for (const p of dependentBundlePaths)
78
- rewriteChains[p] = [];
79
- for (const w of workers) {
80
- rewriteChains[registry.mappedHref(w.dependentEntryPoint)].push(s => s.replace(createWorkerRegex(w.workerUrlPlaceholder), `new Worker('${registry.mappedHref(w.workerEntryPoint)}')`));
54
+ async function rewriteWorkerUrls(build, registry) {
55
+ const workers = registry.workers();
56
+ if (!workers) {
57
+ return;
58
+ }
59
+ const dependentBundlePaths = workers.map((w) => registry.mappedHref(w.dependentEntryPoint));
60
+ const bundleOutputs = {};
61
+ const readingFiles = Promise.all(dependentBundlePaths.map(async (p) => {
62
+ bundleOutputs[p] = await readFile(join(build.dirs.projectRootAbs, build.dirs.buildDist, p), "utf8");
63
+ }));
64
+ const rewriteChains = {};
65
+ for (const p of dependentBundlePaths)
66
+ rewriteChains[p] = [];
67
+ for (const w of workers) {
68
+ rewriteChains[registry.mappedHref(w.dependentEntryPoint)].push((s) => s.replace(createWorkerRegex(w.workerUrlPlaceholder), `new Worker('${registry.mappedHref(w.workerEntryPoint)}')`));
69
+ }
70
+ await readingFiles;
71
+ await Promise.all(Object.entries(bundleOutputs).map(async ([p, content]) => {
72
+ let result = content;
73
+ for (const rewriteFn of rewriteChains[p]) {
74
+ result = rewriteFn(result);
81
75
  }
82
- // wait for file reads
83
- await readingFiles;
84
- // run rewrite regex chain and write back to dist
85
- await Promise.all(Object.entries(bundleOutputs).map(async ([p, content]) => {
86
- let result = content;
87
- for (const rewriteFn of rewriteChains[p]) {
88
- result = rewriteFn(result);
89
- }
90
- await writeFile(join(build.dirs.projectRootAbs, build.dirs.buildDist, p), result);
91
- }));
76
+ await writeFile(join(build.dirs.projectRootAbs, build.dirs.buildDist, p), result);
77
+ }));
92
78
  }
93
- export function createWorkerRegex(workerUrl) {
94
- return new RegExp(`new(?:\\s|\\r?\\n)+Worker(?:\\s|\\r?\\n)*\\((?:\\s|\\r?\\n)*['"]${workerUrl}['"](?:\\s|\\r?\\n)*\\)`, 'g');
79
+ function createWorkerRegex(workerUrl) {
80
+ return new RegExp(`new(?:\\s|\\r?\\n)+Worker(?:\\s|\\r?\\n)*\\((?:\\s|\\r?\\n)*['"]${workerUrl}['"](?:\\s|\\r?\\n)*\\)`, "g");
95
81
  }
82
+ export {
83
+ buildWebsite,
84
+ createWorkerRegex,
85
+ rewriteWorkerUrls
86
+ };
@@ -1,22 +1,21 @@
1
- import { exec } from 'node:child_process';
2
- export async function createBuildTag(build) {
3
- const now = new Date();
4
- const ms = now.getUTCMilliseconds() +
5
- now.getUTCSeconds() * 1000 +
6
- now.getUTCMinutes() * 1000 * 60 +
7
- now.getUTCHours() * 1000 * 60 * 60;
8
- const date = now.toISOString().substring(0, 10);
9
- const time = String(ms).padStart(8, '0');
10
- const when = `${date}-${time}`;
11
- if (build.production) {
12
- const gitHash = await new Promise((res, rej) => exec('git rev-parse --short HEAD', (err, stdout) => {
13
- if (err)
14
- rej(err);
15
- res(stdout.trim());
16
- }));
17
- return `${when}-${gitHash}`;
18
- }
19
- else {
20
- return when;
21
- }
1
+ import { exec } from "node:child_process";
2
+ async function createBuildTag(build) {
3
+ const now = /* @__PURE__ */ new Date();
4
+ const ms = now.getUTCMilliseconds() + now.getUTCSeconds() * 1e3 + now.getUTCMinutes() * 1e3 * 60 + now.getUTCHours() * 1e3 * 60 * 60;
5
+ const date = now.toISOString().substring(0, 10);
6
+ const time = String(ms).padStart(8, "0");
7
+ const when = `${date}-${time}`;
8
+ if (build.production) {
9
+ const gitHash = await new Promise((res, rej) => exec("git rev-parse --short HEAD", (err, stdout) => {
10
+ if (err)
11
+ rej(err);
12
+ res(stdout.trim());
13
+ }));
14
+ return `${when}-${gitHash}`;
15
+ } else {
16
+ return when;
17
+ }
22
18
  }
19
+ export {
20
+ createBuildTag
21
+ };