tachyon-dom 0.1.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.
Files changed (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +339 -0
  3. package/dist/adapters/lambda.d.ts +65 -0
  4. package/dist/adapters/lambda.js +211 -0
  5. package/dist/adapters/node.d.ts +25 -0
  6. package/dist/adapters/node.js +360 -0
  7. package/dist/adapters/workers.d.ts +149 -0
  8. package/dist/adapters/workers.js +272 -0
  9. package/dist/adapters.d.ts +3 -0
  10. package/dist/adapters.js +3 -0
  11. package/dist/app.d.ts +98 -0
  12. package/dist/app.js +306 -0
  13. package/dist/cli.d.ts +55 -0
  14. package/dist/cli.js +711 -0
  15. package/dist/compiler/expression.d.ts +55 -0
  16. package/dist/compiler/expression.js +858 -0
  17. package/dist/compiler/index.d.ts +8 -0
  18. package/dist/compiler/index.js +50 -0
  19. package/dist/compiler/ir.d.ts +4 -0
  20. package/dist/compiler/ir.js +255 -0
  21. package/dist/compiler/parser.d.ts +3 -0
  22. package/dist/compiler/parser.js +236 -0
  23. package/dist/compiler/sfc.d.ts +33 -0
  24. package/dist/compiler/sfc.js +439 -0
  25. package/dist/compiler/targets/client.d.ts +3 -0
  26. package/dist/compiler/targets/client.js +568 -0
  27. package/dist/compiler/targets/server.d.ts +7 -0
  28. package/dist/compiler/targets/server.js +396 -0
  29. package/dist/compiler/targets/stream.d.ts +2 -0
  30. package/dist/compiler/targets/stream.js +204 -0
  31. package/dist/compiler/types.d.ts +185 -0
  32. package/dist/compiler/types.js +1 -0
  33. package/dist/compiler/utils.d.ts +40 -0
  34. package/dist/compiler/utils.js +185 -0
  35. package/dist/compiler/whitespace.d.ts +2 -0
  36. package/dist/compiler/whitespace.js +66 -0
  37. package/dist/compiler.d.ts +1 -0
  38. package/dist/compiler.js +1 -0
  39. package/dist/constant-time.d.ts +1 -0
  40. package/dist/constant-time.js +26 -0
  41. package/dist/cookies.d.ts +44 -0
  42. package/dist/cookies.js +199 -0
  43. package/dist/diagnostics.d.ts +22 -0
  44. package/dist/diagnostics.js +45 -0
  45. package/dist/env.d.ts +27 -0
  46. package/dist/env.js +37 -0
  47. package/dist/html-escape.d.ts +2 -0
  48. package/dist/html-escape.js +20 -0
  49. package/dist/html-whitespace.d.ts +6 -0
  50. package/dist/html-whitespace.js +83 -0
  51. package/dist/i18n.d.ts +19 -0
  52. package/dist/i18n.js +93 -0
  53. package/dist/index.d.ts +20 -0
  54. package/dist/index.js +18 -0
  55. package/dist/language-server.d.ts +17 -0
  56. package/dist/language-server.js +79 -0
  57. package/dist/package-integrity.d.ts +9 -0
  58. package/dist/package-integrity.js +64 -0
  59. package/dist/result.d.ts +13 -0
  60. package/dist/result.js +4 -0
  61. package/dist/router-node.d.ts +2 -0
  62. package/dist/router-node.js +12 -0
  63. package/dist/router.d.ts +324 -0
  64. package/dist/router.js +1024 -0
  65. package/dist/runtime/attr.d.ts +3 -0
  66. package/dist/runtime/attr.js +66 -0
  67. package/dist/runtime/class.d.ts +2 -0
  68. package/dist/runtime/class.js +10 -0
  69. package/dist/runtime/conditional.d.ts +78 -0
  70. package/dist/runtime/conditional.js +140 -0
  71. package/dist/runtime/enhancement.d.ts +11 -0
  72. package/dist/runtime/enhancement.js +70 -0
  73. package/dist/runtime/error-boundary.d.ts +8 -0
  74. package/dist/runtime/error-boundary.js +27 -0
  75. package/dist/runtime/event.d.ts +1 -0
  76. package/dist/runtime/event.js +11 -0
  77. package/dist/runtime/form.d.ts +41 -0
  78. package/dist/runtime/form.js +167 -0
  79. package/dist/runtime/fragment.d.ts +6 -0
  80. package/dist/runtime/fragment.js +18 -0
  81. package/dist/runtime/html.d.ts +8 -0
  82. package/dist/runtime/html.js +6 -0
  83. package/dist/runtime/hydrate.d.ts +68 -0
  84. package/dist/runtime/hydrate.js +205 -0
  85. package/dist/runtime/keyed-rows.d.ts +61 -0
  86. package/dist/runtime/keyed-rows.js +192 -0
  87. package/dist/runtime/list.d.ts +84 -0
  88. package/dist/runtime/list.js +424 -0
  89. package/dist/runtime/portal.d.ts +6 -0
  90. package/dist/runtime/portal.js +12 -0
  91. package/dist/runtime/router.d.ts +102 -0
  92. package/dist/runtime/router.js +649 -0
  93. package/dist/runtime/signal.d.ts +30 -0
  94. package/dist/runtime/signal.js +275 -0
  95. package/dist/runtime/store.d.ts +1 -0
  96. package/dist/runtime/store.js +1 -0
  97. package/dist/runtime/stream-client.d.ts +8 -0
  98. package/dist/runtime/stream-client.js +33 -0
  99. package/dist/runtime/text.d.ts +2 -0
  100. package/dist/runtime/text.js +10 -0
  101. package/dist/runtime/virtual-list.d.ts +15 -0
  102. package/dist/runtime/virtual-list.js +89 -0
  103. package/dist/security.d.ts +27 -0
  104. package/dist/security.js +173 -0
  105. package/dist/server/form-action.d.ts +52 -0
  106. package/dist/server/form-action.js +102 -0
  107. package/dist/server/html.d.ts +19 -0
  108. package/dist/server/html.js +101 -0
  109. package/dist/server/stream.d.ts +4 -0
  110. package/dist/server/stream.js +72 -0
  111. package/dist/source-map.d.ts +17 -0
  112. package/dist/source-map.js +18 -0
  113. package/dist/tachyon-html.d.ts +110 -0
  114. package/dist/testing.d.ts +14 -0
  115. package/dist/testing.js +36 -0
  116. package/dist/typed.d.ts +9 -0
  117. package/dist/typed.js +4 -0
  118. package/dist/vite.d.ts +84 -0
  119. package/dist/vite.js +426 -0
  120. package/package.json +604 -0
package/dist/vite.js ADDED
@@ -0,0 +1,426 @@
1
+ import { cp, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { createNodeFetchHandler } from "./adapters/node.js";
6
+ import { defineApp, generateTachyonModuleTypes, pagesFromRouteFiles, } from "./app.js";
7
+ import { resolveHtmlWhitespacePolicy } from "./html-whitespace.js";
8
+ import { generateScriptOnlyModule, transformSfcScript } from "./compiler/sfc.js";
9
+ import { generateClientModule, generateServerModule, generateServerStreamModule } from "./compiler/index.js";
10
+ import { diagnoseTachyonSfc, diagnosticFromCompilerError, formatDiagnostic } from "./diagnostics.js";
11
+ import { createFileRouteManifest } from "./router.js";
12
+ import { scanFileRoutes } from "./router-node.js";
13
+ import { err, ok } from "./result.js";
14
+ import { appendInlineSourceMap, createSourceMap, shouldEmitSourceMap } from "./source-map.js";
15
+ const configuredAppHtmlWhitespace = (options) => {
16
+ if (options.htmlWhitespace !== undefined)
17
+ return resolveHtmlWhitespacePolicy(options.htmlWhitespace);
18
+ if (options.minifyHtml !== undefined)
19
+ return options.minifyHtml ? "normalize-tags" : "preserve-tags";
20
+ return undefined;
21
+ };
22
+ export const loadRouteApp = async ({ routesDir, ...definition }) => {
23
+ const absoluteRoutesDir = resolve(routesDir);
24
+ const manifest = await scanFileRoutes(absoluteRoutesDir);
25
+ const pageFiles = manifest.filter((route) => route.kind === "template").map((route) => route.file);
26
+ const pages = await Promise.all(pagesFromRouteFiles(pageFiles, {
27
+ rootDir: absoluteRoutesDir,
28
+ ...(definition.templateWhitespace ? { templateWhitespace: definition.templateWhitespace } : {}),
29
+ }).map(async (page) => ({
30
+ ...page,
31
+ template: await readFile(page.file, "utf8"),
32
+ })));
33
+ return defineApp({ ...definition, pages });
34
+ };
35
+ const cloudflarePagesWorkerSource = (options) => {
36
+ const exportName = options.exportName ?? "renderRequest";
37
+ const fallthroughStatuses = options.fallthroughStatuses ?? [404];
38
+ const runtimeEnvKeys = options.runtimeEnvKeys ?? [];
39
+ return `import * as entry from ${JSON.stringify(pathToFileURL(options.entry).href)};
40
+
41
+ const renderer = entry[${JSON.stringify(exportName)}] ?? entry.default;
42
+ const fallthroughStatuses = new Set(${JSON.stringify(fallthroughStatuses)});
43
+ const runtimeEnvKeys = ${JSON.stringify(runtimeEnvKeys)};
44
+
45
+ const runtimeEnvFor = (env) => Object.fromEntries(runtimeEnvKeys.map((key) => [key, env?.[key]]));
46
+
47
+ export default {
48
+ async fetch(request, env = {}, ctx) {
49
+ const assets = env && typeof env === "object" ? env.ASSETS : undefined;
50
+ if (assets && typeof assets.fetch === "function") {
51
+ const assetResponse = await assets.fetch(request);
52
+ if (!fallthroughStatuses.has(assetResponse.status)) {
53
+ return assetResponse;
54
+ }
55
+ }
56
+ if (typeof renderer !== "function") {
57
+ return new Response("Cloudflare Pages renderer export was not found.", { status: 500 });
58
+ }
59
+ return renderer(request, env, ctx, runtimeEnvFor(env));
60
+ },
61
+ };
62
+ `;
63
+ };
64
+ export const packageCloudflarePages = async (options) => {
65
+ const tempDir = await mkdtemp(join(tmpdir(), "tachyon-cloudflare-pages-"));
66
+ try {
67
+ await rm(options.outDir, { recursive: true, force: true });
68
+ await mkdir(options.outDir, { recursive: true });
69
+ if (options.assetsDir) {
70
+ await cp(options.assetsDir, options.outDir, { recursive: true, force: true });
71
+ }
72
+ const workerEntry = join(tempDir, "worker-entry.js");
73
+ await writeFile(workerEntry, cloudflarePagesWorkerSource(options));
74
+ const vite = await import("vite");
75
+ await vite.build({
76
+ configFile: false,
77
+ logLevel: "silent",
78
+ publicDir: false,
79
+ ssr: { noExternal: true },
80
+ build: {
81
+ emptyOutDir: false,
82
+ minify: false,
83
+ outDir: options.outDir,
84
+ sourcemap: false,
85
+ ssr: true,
86
+ target: "es2022",
87
+ lib: {
88
+ entry: workerEntry,
89
+ formats: ["es"],
90
+ fileName: () => "_worker.js",
91
+ },
92
+ rollupOptions: {
93
+ output: {
94
+ entryFileNames: "_worker.js",
95
+ },
96
+ },
97
+ },
98
+ });
99
+ return ok({ outDir: options.outDir, workerPath: join(options.outDir, "_worker.js") });
100
+ }
101
+ catch (error) {
102
+ return err(error instanceof Error ? error.message : String(error));
103
+ }
104
+ finally {
105
+ await rm(tempDir, { recursive: true, force: true });
106
+ }
107
+ };
108
+ const codeForTarget = (target, template, reactive, scriptOnly, defaultScopeName) => {
109
+ if (scriptOnly) {
110
+ return generateScriptOnlyModule(target);
111
+ }
112
+ if (target === "server") {
113
+ return generateServerModule(template);
114
+ }
115
+ if (target === "stream") {
116
+ return generateServerStreamModule(template);
117
+ }
118
+ return generateClientModule(template, { reactive, ...(defaultScopeName ? { defaultScopeName } : {}) });
119
+ };
120
+ const cleanId = (id) => id.split("?", 1)[0] ?? id;
121
+ const queryForId = (id) => new URLSearchParams(id.split("?")[1] ?? "");
122
+ const targetForId = (id, fallback) => {
123
+ const query = queryForId(id);
124
+ if (query.has("server")) {
125
+ return "server";
126
+ }
127
+ if (query.has("stream")) {
128
+ return "stream";
129
+ }
130
+ if (query.has("client")) {
131
+ return "client";
132
+ }
133
+ return fallback;
134
+ };
135
+ const isEntryRequest = (id) => queryForId(id).has("entry");
136
+ const shouldIgnoreQueryRequest = (id) => {
137
+ const query = queryForId(id);
138
+ return query.has("raw") || query.has("url");
139
+ };
140
+ const declarationSourceFor = (source, id) => {
141
+ if (!queryForId(id).has("raw") || !source.trimStart().startsWith("export default")) {
142
+ return source;
143
+ }
144
+ const match = /^\s*export\s+default\s+("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')\s*;?\s*$/s.exec(source);
145
+ if (!match?.[1])
146
+ return source;
147
+ if (match[1].startsWith('"'))
148
+ return JSON.parse(match[1]);
149
+ return match[1].slice(1, -1).replace(/\\'/g, "'").replace(/\\\\/g, "\\");
150
+ };
151
+ const entryCodeFor = (id) => {
152
+ const moduleId = cleanId(id);
153
+ const query = queryForId(id);
154
+ const mountName = query.get("mount") ?? "mount";
155
+ return [
156
+ `import { reportHydrationDiagnostics as __tachyonReportHydrationDiagnostics } from "tachyon-dom/runtime/hydrate";`,
157
+ `import * as module from ${JSON.stringify(moduleId)};`,
158
+ `export * from ${JSON.stringify(moduleId)};`,
159
+ `export default module;`,
160
+ `const root = typeof document === "undefined" ? null : document.querySelector(${JSON.stringify(query.get("root") ?? "#app")});`,
161
+ `const mount = module[${JSON.stringify(mountName)}] ?? module.mountApp ?? module.mountWebExample ?? module.mountAuthTodoExample ?? module.mountFullAppExample ?? module.default;`,
162
+ `if (root instanceof HTMLElement && typeof mount === "function") {`,
163
+ ` void mount(root);`,
164
+ `}`,
165
+ `const __tachyonHydrationIds = (module.hydrationBoundaries ?? []).map((boundary) => boundary && typeof boundary === "object" && boundary.idKind !== "expression" && typeof boundary.id === "string" ? boundary.id : undefined).filter((id) => typeof id === "string");`,
166
+ `if (root instanceof HTMLElement && import.meta.hot && __tachyonHydrationIds.length > 0) {`,
167
+ ` queueMicrotask(() => __tachyonReportHydrationDiagnostics(root, __tachyonHydrationIds, { hot: import.meta.hot }));`,
168
+ `}`,
169
+ ].join("\n");
170
+ };
171
+ const shouldLogRequests = (options) => options === undefined || options === true || (typeof options === "object" && options.enabled !== false);
172
+ const requestLogPath = (url, includeQuery) => {
173
+ if (!url) {
174
+ return "/";
175
+ }
176
+ const parsed = new URL(url, "http://tachyon.local");
177
+ return includeQuery ? `${parsed.pathname}${parsed.search}` : parsed.pathname;
178
+ };
179
+ export const tachyonDom = (options = {}) => {
180
+ const include = options.include ?? /\.(?:td|tachyon(?:\.html)?)$/;
181
+ const target = options.target ?? "client";
182
+ let command = "serve";
183
+ let mode = "development";
184
+ let configResolved = false;
185
+ let rootDir;
186
+ return {
187
+ name: "tachyon-dom",
188
+ enforce: "pre",
189
+ configResolved(config) {
190
+ command = config.command;
191
+ mode = config.mode;
192
+ configResolved = true;
193
+ rootDir = typeof config.root === "string" ? config.root : undefined;
194
+ },
195
+ configureServer(server) {
196
+ if (!shouldLogRequests(options.requestLog)) {
197
+ return;
198
+ }
199
+ const log = typeof options.requestLog === "object" && options.requestLog.logger
200
+ ? options.requestLog.logger
201
+ : (message) => server.config.logger.info(message, { timestamp: true });
202
+ server.middlewares.use((request, response, next) => {
203
+ const startedAt = performance.now();
204
+ const method = request.method ?? "GET";
205
+ const path = requestLogPath(request.url, typeof options.requestLog === "object" && options.requestLog.includeQuery === true);
206
+ response.once("finish", () => {
207
+ const durationMs = Math.max(0, Math.round(performance.now() - startedAt));
208
+ log(`${method} ${path} ${response.statusCode} ${durationMs}ms`);
209
+ });
210
+ next();
211
+ });
212
+ },
213
+ async transform(source, id) {
214
+ if (include.test(cleanId(id)) && !queryForId(id).has("url") && !isEntryRequest(id)) {
215
+ const declarationOutput = options.declarationOutput === false
216
+ ? undefined
217
+ : typeof options.declarationOutput === "function"
218
+ ? options.declarationOutput(cleanId(id))
219
+ : configResolved && rootDir && cleanId(id).startsWith(rootDir)
220
+ ? `${cleanId(id)}.d.ts`
221
+ : undefined;
222
+ if (declarationOutput) {
223
+ const declarationSource = declarationSourceFor(source, id);
224
+ const diagnostic = diagnoseTachyonSfc(declarationSource);
225
+ if (!diagnostic.ok) {
226
+ this.error(formatDiagnostic(diagnostic.error, cleanId(id)));
227
+ }
228
+ const declarations = generateTachyonModuleTypes(declarationSource);
229
+ if (!declarations.ok) {
230
+ this.error(declarations.error);
231
+ }
232
+ await mkdir(dirname(declarationOutput), { recursive: true });
233
+ await writeFile(declarationOutput, declarations.value);
234
+ }
235
+ }
236
+ if (shouldIgnoreQueryRequest(id)) {
237
+ return null;
238
+ }
239
+ if (isEntryRequest(id)) {
240
+ return {
241
+ code: entryCodeFor(id),
242
+ map: null,
243
+ };
244
+ }
245
+ if (!include.test(cleanId(id))) {
246
+ return null;
247
+ }
248
+ const resolvedTarget = targetForId(id, target);
249
+ const result = diagnoseTachyonSfc(source, {
250
+ whitespace: options.templateWhitespace ?? "preserve",
251
+ });
252
+ if (!result.ok) {
253
+ this.error(formatDiagnostic(result.error, id));
254
+ }
255
+ const script = transformSfcScript(result.value.descriptor.script);
256
+ if (!script.ok) {
257
+ this.error(formatDiagnostic(diagnosticFromCompilerError(source, script.error), id));
258
+ }
259
+ const code = `${script.value.code}${codeForTarget(resolvedTarget, result.value.template, options.reactive === true, result.value.scriptOnly, resolvedTarget === "client" && script.value.defaultScopeName ? script.value.defaultScopeName : undefined)}`;
260
+ const emitSourceMap = shouldEmitSourceMap({
261
+ sourcemap: options.sourcemap,
262
+ productionSourceMap: options.productionSourceMap,
263
+ command,
264
+ mode,
265
+ });
266
+ if (!emitSourceMap && (!options.onSourceMap || options.sourcemap === false)) {
267
+ return {
268
+ code,
269
+ map: null,
270
+ };
271
+ }
272
+ const map = createSourceMap(source, id, `${id}.js`);
273
+ await options.onSourceMap?.({ id, code, map, source });
274
+ return {
275
+ code: emitSourceMap ? appendInlineSourceMap(code, map) : code,
276
+ map: null,
277
+ };
278
+ },
279
+ load(id) {
280
+ if (shouldIgnoreQueryRequest(id)) {
281
+ return null;
282
+ }
283
+ if (!include.test(cleanId(id)) || !isEntryRequest(id)) {
284
+ return null;
285
+ }
286
+ return entryCodeFor(id);
287
+ },
288
+ };
289
+ };
290
+ export const tachyonDomRoutes = (options) => {
291
+ const virtualId = options.virtualId ?? "virtual:tachyon-dom/routes";
292
+ const resolvedVirtualId = `\0${virtualId}`;
293
+ const routes = () => [
294
+ ...(options.routes ?? []),
295
+ ...(options.files && options.rootDir
296
+ ? createFileRouteManifest(options.files, { rootDir: options.rootDir }).map((route) => ({
297
+ id: route.id,
298
+ path: route.path,
299
+ module: route.file,
300
+ }))
301
+ : []),
302
+ ];
303
+ return {
304
+ name: "tachyon-dom-routes",
305
+ resolveId(id) {
306
+ return id === virtualId ? resolvedVirtualId : null;
307
+ },
308
+ load(id) {
309
+ if (id !== resolvedVirtualId) {
310
+ return null;
311
+ }
312
+ const modules = routes()
313
+ .map((route) => `{ id: ${JSON.stringify(route.id)}, path: ${JSON.stringify(route.path)}, module: () => import(${JSON.stringify(route.module)}) }`)
314
+ .join(", ");
315
+ return `export const routes = [${modules}];\nexport const manifest = routes.map(({ id, path }) => ({ id, path }));\n`;
316
+ },
317
+ handleHotUpdate(context) {
318
+ const changed = routes().filter((route) => route.module === context.file);
319
+ if (changed.length === 0) {
320
+ return;
321
+ }
322
+ const module = context.server.moduleGraph.getModuleById(resolvedVirtualId);
323
+ if (!module) {
324
+ return;
325
+ }
326
+ context.server.moduleGraph.invalidateModule(module);
327
+ context.server.ws.send({
328
+ type: "custom",
329
+ event: "tachyon-dom:routes-update",
330
+ data: { routeIds: changed.map((route) => route.id) },
331
+ });
332
+ return [module, ...context.modules];
333
+ },
334
+ };
335
+ };
336
+ const prefixed = (prefix, fileName) => `${prefix}/${fileName}`;
337
+ const viteInternalExactPaths = ["/@vite/client", "/@react-refresh", "/__vite_ping"];
338
+ const viteInternalPrefixes = ["/@id/", "/@fs/", "/src/", "/node_modules/"];
339
+ export const isViteSsrPassthroughRequest = (url) => viteInternalExactPaths.includes(url.pathname) ||
340
+ viteInternalPrefixes.some((prefix) => url.pathname.startsWith(prefix));
341
+ const resolveClientScript = async (request, clientScript) => {
342
+ if (typeof clientScript === "function") {
343
+ return clientScript(request);
344
+ }
345
+ return clientScript;
346
+ };
347
+ export const createTachyonSsrMiddleware = (options) => {
348
+ const { fetch, clientScript, bypass = isViteSsrPassthroughRequest, ...adapterOptions } = options;
349
+ const handler = createNodeFetchHandler({
350
+ ...adapterOptions,
351
+ fetch: async (request) => {
352
+ const resolvedClientScript = await resolveClientScript(request, clientScript);
353
+ return fetch(request, resolvedClientScript ? { clientScript: resolvedClientScript } : {});
354
+ },
355
+ });
356
+ return (request, response, next) => {
357
+ let url;
358
+ try {
359
+ url = new URL(request.url ?? "/", "http://tachyon.local");
360
+ }
361
+ catch {
362
+ next();
363
+ return;
364
+ }
365
+ if (bypass(url, request)) {
366
+ next();
367
+ return;
368
+ }
369
+ void handler(request, response).catch((error) => next(error));
370
+ };
371
+ };
372
+ export const tachyonSsr = (options) => ({
373
+ name: "tachyon-dom-ssr",
374
+ configureServer(server) {
375
+ const { staticAssets = { rootDir: server.config.publicDir, basePath: "/", fallthroughOnNotFound: true }, ...rest } = options;
376
+ server.middlewares.use(createTachyonSsrMiddleware({
377
+ ...rest,
378
+ ...(staticAssets === false ? {} : { staticAssets }),
379
+ }));
380
+ },
381
+ });
382
+ export const tachyonApp = (app, options = {}) => {
383
+ const configuredWhitespace = configuredAppHtmlWhitespace(options);
384
+ const whitespaceFor = (mode) => configuredWhitespace ?? (mode === "development" ? "preserve-tags" : "normalize-tags");
385
+ return {
386
+ name: "tachyon-dom-app",
387
+ configureServer(server) {
388
+ server.middlewares.use((request, response, next) => {
389
+ const page = app.pageForPath(new URL(request.url ?? "/", "http://tachyon.local").pathname);
390
+ if (!page) {
391
+ next();
392
+ return;
393
+ }
394
+ response.statusCode = 200;
395
+ response.setHeader("Content-Type", "text/html; charset=utf-8");
396
+ response.end(app.renderDocument(page.path, {
397
+ assets: {
398
+ scripts: [options.appScript ?? `${page.assetPrefix ?? "."}/main.ts`],
399
+ styles: [`${page.assetPrefix ?? "."}/styles.css`],
400
+ },
401
+ whitespace: whitespaceFor("development"),
402
+ }));
403
+ });
404
+ },
405
+ generateBundle(_outputOptions, bundle) {
406
+ const entry = Object.values(bundle).find((item) => item.type === "chunk" && item.isEntry);
407
+ const cssFiles = Object.values(bundle).flatMap((item) => item.type === "asset" && item.fileName.endsWith(".css") ? [item.fileName] : []);
408
+ for (const page of app.pages) {
409
+ const prefix = page.assetPrefix ?? ".";
410
+ const assets = {
411
+ scripts: entry && entry.type === "chunk" ? [prefixed(prefix, entry.fileName)] : [],
412
+ styles: cssFiles.map((fileName) => prefixed(prefix, fileName)),
413
+ };
414
+ this.emitFile({
415
+ fileName: page.fileName,
416
+ source: app.renderDocument(page.path, {
417
+ assets,
418
+ whitespace: whitespaceFor("production"),
419
+ }),
420
+ type: "asset",
421
+ });
422
+ }
423
+ },
424
+ };
425
+ };
426
+ export default tachyonDom;