@taujs/server 0.2.1 → 0.2.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.
@@ -0,0 +1,15 @@
1
+ import { PluginOption } from 'vite';
2
+
3
+ type Config = {
4
+ appId: string;
5
+ entryPoint: string;
6
+ plugins?: PluginOption[];
7
+ };
8
+ declare function taujsBuild({ config, projectRoot, clientBaseDir, isSSRBuild, }: {
9
+ config: Config[];
10
+ projectRoot: string;
11
+ clientBaseDir: string;
12
+ isSSRBuild?: boolean;
13
+ }): Promise<void>;
14
+
15
+ export { type Config, taujsBuild };
package/dist/build.js ADDED
@@ -0,0 +1,669 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ // node_modules/fastify-plugin/lib/getPluginName.js
28
+ var require_getPluginName = __commonJS({
29
+ "node_modules/fastify-plugin/lib/getPluginName.js"(exports, module) {
30
+ "use strict";
31
+ var fpStackTracePattern = /at\s{1}(?:.*\.)?plugin\s{1}.*\n\s*(.*)/;
32
+ var fileNamePattern = /(\w*(\.\w*)*)\..*/;
33
+ module.exports = function getPluginName(fn) {
34
+ if (fn.name.length > 0) return fn.name;
35
+ const stackTraceLimit = Error.stackTraceLimit;
36
+ Error.stackTraceLimit = 10;
37
+ try {
38
+ throw new Error("anonymous function");
39
+ } catch (e) {
40
+ Error.stackTraceLimit = stackTraceLimit;
41
+ return extractPluginName(e.stack);
42
+ }
43
+ };
44
+ function extractPluginName(stack) {
45
+ const m = stack.match(fpStackTracePattern);
46
+ return m ? m[1].split(/[/\\]/).slice(-1)[0].match(fileNamePattern)[1] : "anonymous";
47
+ }
48
+ module.exports.extractPluginName = extractPluginName;
49
+ }
50
+ });
51
+
52
+ // node_modules/fastify-plugin/lib/toCamelCase.js
53
+ var require_toCamelCase = __commonJS({
54
+ "node_modules/fastify-plugin/lib/toCamelCase.js"(exports, module) {
55
+ "use strict";
56
+ module.exports = function toCamelCase(name) {
57
+ if (name[0] === "@") {
58
+ name = name.slice(1).replace("/", "-");
59
+ }
60
+ return name.replace(/-(.)/g, function(match2, g1) {
61
+ return g1.toUpperCase();
62
+ });
63
+ };
64
+ }
65
+ });
66
+
67
+ // node_modules/fastify-plugin/plugin.js
68
+ var require_plugin = __commonJS({
69
+ "node_modules/fastify-plugin/plugin.js"(exports, module) {
70
+ "use strict";
71
+ var getPluginName = require_getPluginName();
72
+ var toCamelCase = require_toCamelCase();
73
+ var count = 0;
74
+ function plugin(fn, options = {}) {
75
+ let autoName = false;
76
+ if (fn.default !== void 0) {
77
+ fn = fn.default;
78
+ }
79
+ if (typeof fn !== "function") {
80
+ throw new TypeError(
81
+ `fastify-plugin expects a function, instead got a '${typeof fn}'`
82
+ );
83
+ }
84
+ if (typeof options === "string") {
85
+ options = {
86
+ fastify: options
87
+ };
88
+ }
89
+ if (typeof options !== "object" || Array.isArray(options) || options === null) {
90
+ throw new TypeError("The options object should be an object");
91
+ }
92
+ if (options.fastify !== void 0 && typeof options.fastify !== "string") {
93
+ throw new TypeError(`fastify-plugin expects a version string, instead got '${typeof options.fastify}'`);
94
+ }
95
+ if (!options.name) {
96
+ autoName = true;
97
+ options.name = getPluginName(fn) + "-auto-" + count++;
98
+ }
99
+ fn[Symbol.for("skip-override")] = options.encapsulate !== true;
100
+ fn[Symbol.for("fastify.display-name")] = options.name;
101
+ fn[Symbol.for("plugin-meta")] = options;
102
+ if (!fn.default) {
103
+ fn.default = fn;
104
+ }
105
+ const camelCase = toCamelCase(options.name);
106
+ if (!autoName && !fn[camelCase]) {
107
+ fn[camelCase] = fn;
108
+ }
109
+ return fn;
110
+ }
111
+ module.exports = plugin;
112
+ module.exports.default = plugin;
113
+ module.exports.fastifyPlugin = plugin;
114
+ }
115
+ });
116
+
117
+ // node_modules/picocolors/picocolors.js
118
+ var require_picocolors = __commonJS({
119
+ "node_modules/picocolors/picocolors.js"(exports, module) {
120
+ "use strict";
121
+ var p = process || {};
122
+ var argv = p.argv || [];
123
+ var env = p.env || {};
124
+ var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
125
+ var formatter = (open, close, replace = open) => (input) => {
126
+ let string = "" + input, index = string.indexOf(close, open.length);
127
+ return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
128
+ };
129
+ var replaceClose = (string, close, replace, index) => {
130
+ let result = "", cursor = 0;
131
+ do {
132
+ result += string.substring(cursor, index) + replace;
133
+ cursor = index + close.length;
134
+ index = string.indexOf(close, cursor);
135
+ } while (~index);
136
+ return result + string.substring(cursor);
137
+ };
138
+ var createColors = (enabled = isColorSupported) => {
139
+ let f = enabled ? formatter : () => String;
140
+ return {
141
+ isColorSupported: enabled,
142
+ reset: f("\x1B[0m", "\x1B[0m"),
143
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
144
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
145
+ italic: f("\x1B[3m", "\x1B[23m"),
146
+ underline: f("\x1B[4m", "\x1B[24m"),
147
+ inverse: f("\x1B[7m", "\x1B[27m"),
148
+ hidden: f("\x1B[8m", "\x1B[28m"),
149
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
150
+ black: f("\x1B[30m", "\x1B[39m"),
151
+ red: f("\x1B[31m", "\x1B[39m"),
152
+ green: f("\x1B[32m", "\x1B[39m"),
153
+ yellow: f("\x1B[33m", "\x1B[39m"),
154
+ blue: f("\x1B[34m", "\x1B[39m"),
155
+ magenta: f("\x1B[35m", "\x1B[39m"),
156
+ cyan: f("\x1B[36m", "\x1B[39m"),
157
+ white: f("\x1B[37m", "\x1B[39m"),
158
+ gray: f("\x1B[90m", "\x1B[39m"),
159
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
160
+ bgRed: f("\x1B[41m", "\x1B[49m"),
161
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
162
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
163
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
164
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
165
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
166
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
167
+ blackBright: f("\x1B[90m", "\x1B[39m"),
168
+ redBright: f("\x1B[91m", "\x1B[39m"),
169
+ greenBright: f("\x1B[92m", "\x1B[39m"),
170
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
171
+ blueBright: f("\x1B[94m", "\x1B[39m"),
172
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
173
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
174
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
175
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
176
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
177
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
178
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
179
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
180
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
181
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
182
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m")
183
+ };
184
+ };
185
+ module.exports = createColors();
186
+ module.exports.createColors = createColors;
187
+ }
188
+ });
189
+
190
+ // src/build.ts
191
+ import fs from "fs/promises";
192
+ import path3 from "path";
193
+ import { build } from "vite";
194
+ import { nodePolyfills } from "vite-plugin-node-polyfills";
195
+
196
+ // src/SSRServer.ts
197
+ var import_fastify_plugin = __toESM(require_plugin(), 1);
198
+ var import_picocolors = __toESM(require_picocolors(), 1);
199
+ import { readFile } from "fs/promises";
200
+ import path2 from "path";
201
+
202
+ // src/constants.ts
203
+ var RENDERTYPE = {
204
+ ssr: "ssr",
205
+ streaming: "streaming"
206
+ };
207
+ var SSRTAG = {
208
+ ssrHead: "<!--ssr-head-->",
209
+ ssrHtml: "<!--ssr-html-->"
210
+ };
211
+ var TEMPLATE = {
212
+ defaultEntryClient: "entry-client",
213
+ defaultEntryServer: "entry-server",
214
+ defaultHtmlTemplate: "index.html"
215
+ };
216
+
217
+ // src/utils/Utils.ts
218
+ import { dirname, join } from "path";
219
+ import "path";
220
+ import { fileURLToPath } from "url";
221
+ import { match } from "path-to-regexp";
222
+ var isDevelopment = process.env.NODE_ENV === "development";
223
+ var __filename = fileURLToPath(import.meta.url);
224
+ var __dirname = join(dirname(__filename), !isDevelopment ? "./" : "..");
225
+ var CSS_LANGS_RE = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
226
+ async function collectStyle(server, entries) {
227
+ const urls = await collectStyleUrls(server, entries);
228
+ const codes = await Promise.all(
229
+ urls.map(async (url) => {
230
+ const res = await server.transformRequest(url + "?direct");
231
+ return [`/* [collectStyle] ${url} */`, res?.code];
232
+ })
233
+ );
234
+ return codes.flat().filter(Boolean).join("\n\n");
235
+ }
236
+ async function collectStyleUrls(server, entries) {
237
+ const visited = /* @__PURE__ */ new Set();
238
+ async function traverse(url) {
239
+ const [, id] = await server.moduleGraph.resolveUrl(url);
240
+ if (visited.has(id)) return;
241
+ visited.add(id);
242
+ const mod = server.moduleGraph.getModuleById(id);
243
+ if (!mod) return;
244
+ await Promise.all([...mod.importedModules].map((childMod) => traverse(childMod.url)));
245
+ }
246
+ await Promise.all(entries.map((e) => server.transformRequest(e)));
247
+ await Promise.all(entries.map((url) => traverse(url)));
248
+ return [...visited].filter((url) => url.match(CSS_LANGS_RE));
249
+ }
250
+ function renderPreloadLinks(ssrManifest, basePath = "") {
251
+ const seen = /* @__PURE__ */ new Set();
252
+ let links = "";
253
+ for (const moduleId in ssrManifest) {
254
+ const files = ssrManifest[moduleId];
255
+ if (files) {
256
+ files.forEach((file) => {
257
+ if (!seen.has(file)) {
258
+ seen.add(file);
259
+ links += renderPreloadLink(basePath ? `${basePath}/${file}` : `${file}`);
260
+ }
261
+ });
262
+ }
263
+ }
264
+ return links;
265
+ }
266
+ function renderPreloadLink(file) {
267
+ const fileType = file.match(/\.(js|css|woff2?|gif|jpe?g|png|svg)$/)?.[1];
268
+ switch (fileType) {
269
+ case "js":
270
+ return `<link rel="modulepreload" href="${file}">`;
271
+ case "css":
272
+ return `<link rel="stylesheet" href="${file}">`;
273
+ case "woff":
274
+ case "woff2":
275
+ return `<link rel="preload" href="${file}" as="font" type="font/${fileType}" crossorigin>`;
276
+ case "gif":
277
+ case "jpeg":
278
+ case "jpg":
279
+ case "png":
280
+ return `<link rel="preload" href="${file}" as="image" type="image/${fileType}">`;
281
+ case "svg":
282
+ return `<link rel="preload" href="${file}" as="image" type="image/svg+xml">`;
283
+ default:
284
+ return "";
285
+ }
286
+ }
287
+ var callServiceMethod = async (serviceRegistry, serviceName, serviceMethod, params) => {
288
+ const service = serviceRegistry[serviceName];
289
+ if (service && typeof service[serviceMethod] === "function") {
290
+ const method = service[serviceMethod];
291
+ const data = await method(params);
292
+ if (typeof data !== "object" || data === null)
293
+ throw new Error(`Expected object response from service method ${serviceMethod} on ${serviceName}, but got ${typeof data}`);
294
+ return data;
295
+ }
296
+ throw new Error(`Service method ${serviceMethod} does not exist on ${serviceName}`);
297
+ };
298
+ var fetchData = async ({ url, options }) => {
299
+ if (url) {
300
+ const response = await fetch(url, options);
301
+ if (!response.ok) throw new Error(`Failed to fetch data from ${url}`);
302
+ const data = await response.json();
303
+ if (typeof data !== "object" || data === null) throw new Error(`Expected object response from ${url}, but got ${typeof data}`);
304
+ return data;
305
+ }
306
+ throw new Error("URL must be provided to fetch data");
307
+ };
308
+ var fetchInitialData = async (attr, params, serviceRegistry) => {
309
+ if (attr && typeof attr.fetch === "function") {
310
+ return attr.fetch(params, {
311
+ headers: { "Content-Type": "application/json" },
312
+ params
313
+ }).then(async (data) => {
314
+ if (data.serviceName && data.serviceMethod) {
315
+ return await callServiceMethod(serviceRegistry, data.serviceName, data.serviceMethod, data.options?.params ?? {});
316
+ }
317
+ return await fetchData(data);
318
+ }).catch((error) => {
319
+ console.error("Error fetching initial data:", error);
320
+ throw error;
321
+ });
322
+ }
323
+ return Promise.resolve({});
324
+ };
325
+ var matchRoute = (url, renderRoutes) => {
326
+ for (const route of renderRoutes) {
327
+ const matcher = match(route.path, {
328
+ decode: decodeURIComponent
329
+ });
330
+ const matched = matcher(url);
331
+ if (matched) return { route, params: matched.params };
332
+ }
333
+ return null;
334
+ };
335
+ function getCssLinks(manifest, basePath = "") {
336
+ const seen = /* @__PURE__ */ new Set();
337
+ const styles = [];
338
+ for (const key in manifest) {
339
+ const entry = manifest[key];
340
+ if (entry && entry.css) {
341
+ for (const cssFile of entry.css) {
342
+ if (!seen.has(cssFile)) {
343
+ seen.add(cssFile);
344
+ styles.push(`<link rel="preload stylesheet" as="style" type="text/css" href="${basePath}/${cssFile}">`);
345
+ }
346
+ }
347
+ }
348
+ }
349
+ return styles.join("\n");
350
+ }
351
+ var overrideCSSHMRConsoleError = () => {
352
+ const originalConsoleError = console.error;
353
+ console.error = function(message, ...optionalParams) {
354
+ if (typeof message === "string" && message.includes("css hmr is not supported in runtime mode")) return;
355
+ originalConsoleError.apply(console, [message, ...optionalParams]);
356
+ };
357
+ };
358
+ var ensureNonNull = (value, errorMessage) => {
359
+ if (value === void 0 || value === null) throw new Error(errorMessage);
360
+ return value;
361
+ };
362
+
363
+ // src/SSRServer.ts
364
+ var createMaps = () => {
365
+ return {
366
+ bootstrapModules: /* @__PURE__ */ new Map(),
367
+ cssLinks: /* @__PURE__ */ new Map(),
368
+ manifests: /* @__PURE__ */ new Map(),
369
+ preloadLinks: /* @__PURE__ */ new Map(),
370
+ renderModules: /* @__PURE__ */ new Map(),
371
+ ssrManifests: /* @__PURE__ */ new Map(),
372
+ templates: /* @__PURE__ */ new Map()
373
+ };
374
+ };
375
+ var processConfigs = (configs, baseClientRoot, templateDefaults) => {
376
+ return configs.map((config) => {
377
+ const clientRoot = path2.resolve(baseClientRoot, config.entryPoint);
378
+ return {
379
+ clientRoot,
380
+ entryPoint: config.entryPoint,
381
+ entryClient: config.entryClient || templateDefaults.defaultEntryClient,
382
+ entryServer: config.entryServer || templateDefaults.defaultEntryServer,
383
+ htmlTemplate: config.htmlTemplate || templateDefaults.defaultHtmlTemplate,
384
+ appId: config.appId
385
+ };
386
+ });
387
+ };
388
+ var SSRServer = (0, import_fastify_plugin.default)(
389
+ async (app, opts) => {
390
+ const { alias, configs, routes, serviceRegistry, isDebug, clientRoot: baseClientRoot } = opts;
391
+ const { bootstrapModules, cssLinks, manifests, preloadLinks, renderModules, ssrManifests, templates } = createMaps();
392
+ const processedConfigs = processConfigs(configs, baseClientRoot, TEMPLATE);
393
+ for (const config of processedConfigs) {
394
+ const { clientRoot, entryClient, htmlTemplate } = config;
395
+ const templateHtmlPath = path2.join(clientRoot, htmlTemplate);
396
+ const templateHtml = await readFile(templateHtmlPath, "utf-8");
397
+ templates.set(clientRoot, templateHtml);
398
+ const relativeBasePath = path2.relative(baseClientRoot, clientRoot).replace(/\\/g, "/");
399
+ const adjustedRelativePath = relativeBasePath ? `/${relativeBasePath}` : "";
400
+ if (!isDevelopment) {
401
+ const manifestPath = path2.join(clientRoot, ".vite/manifest.json");
402
+ const manifestContent = await readFile(manifestPath, "utf-8");
403
+ const manifest = JSON.parse(manifestContent);
404
+ manifests.set(clientRoot, manifest);
405
+ const ssrManifestPath = path2.join(clientRoot, ".vite/ssr-manifest.json");
406
+ const ssrManifestContent = await readFile(ssrManifestPath, "utf-8");
407
+ const ssrManifest = JSON.parse(ssrManifestContent);
408
+ ssrManifests.set(clientRoot, ssrManifest);
409
+ const entryClientFile = manifest[`${entryClient}.tsx`]?.file;
410
+ if (!entryClientFile) throw new Error(`Entry client file not found in manifest for ${entryClient}.tsx`);
411
+ const bootstrapModule = `/${adjustedRelativePath}/${entryClientFile}`.replace(/\/{2,}/g, "/");
412
+ bootstrapModules.set(clientRoot, bootstrapModule);
413
+ const preloadLink = renderPreloadLinks(ssrManifest, adjustedRelativePath);
414
+ preloadLinks.set(clientRoot, preloadLink);
415
+ const cssLink = getCssLinks(manifest, adjustedRelativePath);
416
+ cssLinks.set(clientRoot, cssLink);
417
+ } else {
418
+ const bootstrapModule = `/${adjustedRelativePath}/${entryClient}`.replace(/\/{2,}/g, "/");
419
+ bootstrapModules.set(clientRoot, bootstrapModule);
420
+ }
421
+ }
422
+ let viteDevServer;
423
+ await app.register(import("@fastify/static"), {
424
+ index: false,
425
+ prefix: "/",
426
+ root: baseClientRoot,
427
+ wildcard: false
428
+ });
429
+ if (isDevelopment) {
430
+ const { createServer } = await import("vite");
431
+ viteDevServer = await createServer({
432
+ appType: "custom",
433
+ css: {
434
+ preprocessorOptions: {
435
+ scss: {
436
+ api: "modern-compiler"
437
+ }
438
+ }
439
+ },
440
+ mode: "development",
441
+ plugins: [
442
+ ...isDebug ? [
443
+ {
444
+ name: "taujs-development-server-debug-logging",
445
+ configureServer(server) {
446
+ console.log(import_picocolors.default.green("\u03C4js development server debug started."));
447
+ server.middlewares.use((req, res, next) => {
448
+ console.log(import_picocolors.default.cyan(`\u2190 rx: ${req.url}`));
449
+ res.on("finish", () => console.log(import_picocolors.default.yellow(`\u2192 tx: ${req.url}`)));
450
+ next();
451
+ });
452
+ }
453
+ }
454
+ ] : []
455
+ ],
456
+ resolve: {
457
+ alias: {
458
+ "@client": path2.resolve(baseClientRoot),
459
+ "@server": path2.resolve(__dirname),
460
+ "@shared": path2.resolve(__dirname, "../shared"),
461
+ ...alias
462
+ }
463
+ },
464
+ root: baseClientRoot,
465
+ server: {
466
+ middlewareMode: true,
467
+ hmr: {
468
+ port: 5174
469
+ }
470
+ }
471
+ });
472
+ overrideCSSHMRConsoleError();
473
+ app.addHook("onRequest", async (request, reply) => {
474
+ await new Promise((resolve) => {
475
+ viteDevServer.middlewares(request.raw, reply.raw, () => {
476
+ if (!reply.sent) resolve();
477
+ });
478
+ });
479
+ });
480
+ }
481
+ app.get("/*", async (req, reply) => {
482
+ try {
483
+ if (/\.\w+$/.test(req.raw.url ?? "")) return reply.callNotFound();
484
+ const url = req.url ? new URL(req.url, `http://${req.headers.host}`).pathname : "/";
485
+ const matchedRoute = matchRoute(url, routes);
486
+ if (!matchedRoute) {
487
+ reply.callNotFound();
488
+ return;
489
+ }
490
+ const { route, params } = matchedRoute;
491
+ const { attr, appId } = route;
492
+ const config = processedConfigs.find((config2) => config2.appId === appId) || processedConfigs[0];
493
+ if (!config) throw new Error("No configuration found for the request.");
494
+ const { clientRoot, entryServer } = config;
495
+ let template = ensureNonNull(templates.get(clientRoot), `Template not found for clientRoot: ${clientRoot}`);
496
+ const bootstrapModule = bootstrapModules.get(clientRoot);
497
+ const cssLink = cssLinks.get(clientRoot);
498
+ const manifest = manifests.get(clientRoot);
499
+ const preloadLink = preloadLinks.get(clientRoot);
500
+ const ssrManifest = ssrManifests.get(clientRoot);
501
+ let renderModule;
502
+ if (isDevelopment) {
503
+ template = template.replace(/<script type="module" src="\/@vite\/client"><\/script>/g, "");
504
+ template = template.replace(/<style type="text\/css">[\s\S]*?<\/style>/g, "");
505
+ const entryServerPath = path2.join(clientRoot, `${entryServer}.tsx`);
506
+ const executedModule = await viteDevServer.ssrLoadModule(entryServerPath);
507
+ renderModule = executedModule;
508
+ const styles = await collectStyle(viteDevServer, [entryServerPath]);
509
+ template = template?.replace("</head>", `<style type="text/css">${styles}</style></head>`);
510
+ template = await viteDevServer.transformIndexHtml(url, template);
511
+ } else {
512
+ renderModule = renderModules.get(clientRoot);
513
+ if (!renderModule) {
514
+ const renderModulePath = path2.join(clientRoot, `${entryServer}.js`);
515
+ const importedModule = await import(renderModulePath);
516
+ renderModule = importedModule;
517
+ renderModules.set(clientRoot, renderModule);
518
+ }
519
+ }
520
+ const renderType = attr?.render || RENDERTYPE.ssr;
521
+ const [beforeBody = "", afterBody = ""] = template.split(SSRTAG.ssrHtml);
522
+ const [beforeHead = "", afterHead = ""] = beforeBody.split(SSRTAG.ssrHead);
523
+ const initialDataPromise = fetchInitialData(attr, params, serviceRegistry);
524
+ if (renderType === RENDERTYPE.ssr) {
525
+ const { renderSSR } = renderModule;
526
+ const initialDataResolved = await initialDataPromise;
527
+ const initialDataScript = `<script>window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`;
528
+ const { headContent, appHtml } = await renderSSR(initialDataResolved, req.url, attr?.meta);
529
+ let aggregateHeadContent = headContent;
530
+ if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
531
+ if (manifest && cssLink) aggregateHeadContent += cssLink;
532
+ const fullHtml = template.replace(SSRTAG.ssrHead, aggregateHeadContent).replace(SSRTAG.ssrHtml, `${appHtml}${initialDataScript}<script type="module" src="${bootstrapModule}" defer></script>`);
533
+ return reply.status(200).header("Content-Type", "text/html").send(fullHtml);
534
+ } else {
535
+ const { renderStream } = renderModule;
536
+ reply.raw.writeHead(200, { "Content-Type": "text/html" });
537
+ renderStream(
538
+ reply.raw,
539
+ {
540
+ onHead: (headContent) => {
541
+ let aggregateHeadContent = headContent;
542
+ if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
543
+ if (manifest && cssLink) aggregateHeadContent += cssLink;
544
+ reply.raw.write(`${beforeHead}${aggregateHeadContent}${afterHead}`);
545
+ },
546
+ onFinish: async (initialDataResolved) => {
547
+ reply.raw.write(`<script>window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`);
548
+ reply.raw.write(afterBody);
549
+ reply.raw.end();
550
+ },
551
+ onError: (error) => {
552
+ console.error("Critical rendering onError:", error);
553
+ reply.raw.end("Internal Server Error");
554
+ }
555
+ },
556
+ initialDataPromise,
557
+ req.url,
558
+ bootstrapModule,
559
+ attr?.meta
560
+ );
561
+ }
562
+ } catch (error) {
563
+ console.error("Error setting up SSR stream:", error);
564
+ if (!reply.raw.headersSent) reply.raw.writeHead(500, { "Content-Type": "text/plain" });
565
+ reply.raw.end("Internal Server Error");
566
+ }
567
+ });
568
+ app.setNotFoundHandler(async (req, reply) => {
569
+ if (/\.\w+$/.test(req.raw.url ?? "")) return reply.callNotFound();
570
+ try {
571
+ const defaultConfig = processedConfigs[0];
572
+ if (!defaultConfig) throw new Error("No default configuration found.");
573
+ const { clientRoot } = defaultConfig;
574
+ let template = ensureNonNull(templates.get(clientRoot), `Template not found for clientRoot: ${clientRoot}`);
575
+ const cssLink = cssLinks.get(clientRoot);
576
+ const bootstrapModule = bootstrapModules.get(clientRoot);
577
+ template = template.replace(SSRTAG.ssrHead, "").replace(SSRTAG.ssrHtml, "");
578
+ if (!isDevelopment && cssLink) template = template.replace("</head>", `${cssLink}</head>`);
579
+ if (bootstrapModule) template = template.replace("</body>", `<script type="module" src="${bootstrapModule}" defer></script></body>`);
580
+ reply.status(200).type("text/html").send(template);
581
+ } catch (error) {
582
+ console.error("Failed to serve clientHtmlTemplate:", error);
583
+ reply.status(500).send("Internal Server Error");
584
+ }
585
+ });
586
+ },
587
+ { name: "taujs-ssr-server" }
588
+ );
589
+
590
+ // src/build.ts
591
+ async function taujsBuild({
592
+ config,
593
+ projectRoot,
594
+ clientBaseDir,
595
+ isSSRBuild = process.env.BUILD_MODE === "ssr"
596
+ }) {
597
+ const deleteDist = async () => {
598
+ const distPath = path3.resolve(projectRoot, "dist");
599
+ try {
600
+ await fs.rm(distPath, { recursive: true, force: true });
601
+ console.log("Deleted the dist directory\n");
602
+ } catch (err) {
603
+ console.error("Error deleting dist directory:", err);
604
+ }
605
+ };
606
+ const processedConfigs = processConfigs(config, clientBaseDir, TEMPLATE);
607
+ if (!isSSRBuild) await deleteDist();
608
+ for (const config2 of processedConfigs) {
609
+ const { appId, entryPoint, clientRoot, entryClient, entryServer, htmlTemplate, plugins = [] } = config2;
610
+ const outDir = path3.resolve(projectRoot, `dist/client/${entryPoint}`);
611
+ const root = entryPoint ? path3.resolve(clientBaseDir, entryPoint) : clientBaseDir;
612
+ const server = path3.resolve(clientRoot, `${entryServer}.tsx`);
613
+ const client = path3.resolve(clientRoot, `${entryClient}.tsx`);
614
+ const main = path3.resolve(clientRoot, htmlTemplate);
615
+ const viteConfig = {
616
+ base: entryPoint ? `/${entryPoint}/` : "/",
617
+ build: {
618
+ outDir,
619
+ manifest: !isSSRBuild,
620
+ rollupOptions: {
621
+ input: isSSRBuild ? { server } : { client, main }
622
+ },
623
+ ssr: isSSRBuild ? server : void 0,
624
+ ssrManifest: isSSRBuild,
625
+ ...isSSRBuild && {
626
+ format: "esm",
627
+ target: `node${process.versions.node.split(".").map(Number)[0]}`
628
+ }
629
+ },
630
+ css: {
631
+ preprocessorOptions: {
632
+ scss: { api: "modern-compiler" }
633
+ }
634
+ },
635
+ plugins: [...config2.plugins ?? [], nodePolyfills({ include: ["fs", "stream"] })],
636
+ publicDir: "public",
637
+ resolve: {
638
+ alias: {
639
+ "@client": root,
640
+ "@server": path3.resolve(projectRoot, "src/server"),
641
+ "@shared": path3.resolve(projectRoot, "src/shared")
642
+ }
643
+ },
644
+ root,
645
+ server: {
646
+ proxy: {
647
+ "/api": {
648
+ target: "http://localhost:3000",
649
+ changeOrigin: true,
650
+ rewrite: (path4) => path4.replace(/^\/api/, "")
651
+ }
652
+ }
653
+ }
654
+ };
655
+ try {
656
+ console.log(`Building for entryPoint: "${entryPoint}" (${appId})`);
657
+ await build(viteConfig);
658
+ console.log(`Build complete for entryPoint: "${entryPoint}"
659
+ `);
660
+ } catch (error) {
661
+ console.error(`Error building for entryPoint: "${entryPoint}"
662
+ `, error);
663
+ process.exit(1);
664
+ }
665
+ }
666
+ }
667
+ export {
668
+ taujsBuild
669
+ };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ServerResponse } from 'node:http';
2
2
  import { FastifyPluginAsync } from 'fastify';
3
+ import { PluginOption } from 'vite';
3
4
 
4
5
  declare const RENDERTYPE: {
5
6
  ssr: string;
@@ -36,6 +37,7 @@ type ProcessedConfig = {
36
37
  entryPoint: string;
37
38
  entryServer: string;
38
39
  htmlTemplate: string;
40
+ plugins?: PluginOption[];
39
41
  };
40
42
  type SSRServerOptions = {
41
43
  alias?: Record<string, string>;
package/dist/index.js CHANGED
@@ -523,7 +523,7 @@ var SSRServer = (0, import_fastify_plugin.default)(
523
523
  let aggregateHeadContent = headContent;
524
524
  if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
525
525
  if (manifest && cssLink) aggregateHeadContent += cssLink;
526
- const fullHtml = template.replace(SSRTAG.ssrHead, aggregateHeadContent).replace(SSRTAG.ssrHtml, `${appHtml}${initialDataScript}<script type="module" src="${bootstrapModule}" async=""></script>`);
526
+ const fullHtml = template.replace(SSRTAG.ssrHead, aggregateHeadContent).replace(SSRTAG.ssrHtml, `${appHtml}${initialDataScript}<script type="module" src="${bootstrapModule}" defer></script>`);
527
527
  return reply.status(200).header("Content-Type", "text/html").send(fullHtml);
528
528
  } else {
529
529
  const { renderStream } = renderModule;
@@ -570,7 +570,7 @@ var SSRServer = (0, import_fastify_plugin.default)(
570
570
  const bootstrapModule = bootstrapModules.get(clientRoot);
571
571
  template = template.replace(SSRTAG.ssrHead, "").replace(SSRTAG.ssrHtml, "");
572
572
  if (!isDevelopment && cssLink) template = template.replace("</head>", `${cssLink}</head>`);
573
- if (bootstrapModule) template = template.replace("</body>", `<script type="module" src="${bootstrapModule}" async=""></script></body>`);
573
+ if (bootstrapModule) template = template.replace("</body>", `<script type="module" src="${bootstrapModule}" defer></script></body>`);
574
574
  reply.status(200).type("text/html").send(template);
575
575
  } catch (error) {
576
576
  console.error("Failed to serve clientHtmlTemplate:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taujs/server",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "taujs | τjs",
5
5
  "author": "Aoede <taujs@aoede.uk.net> (https://www.aoede.uk.net)",
6
6
  "license": "MIT",
@@ -29,9 +29,9 @@
29
29
  "import": "./dist/index.js",
30
30
  "types": "./dist/index.d.ts"
31
31
  },
32
- "./data": {
33
- "import": "./dist/data.js",
34
- "types": "./dist/data.d.ts"
32
+ "./build": {
33
+ "import": "./dist/build.js",
34
+ "types": "./dist/build.d.ts"
35
35
  },
36
36
  "./package.json": "./package.json"
37
37
  },
@@ -40,22 +40,18 @@
40
40
  ],
41
41
  "dependencies": {
42
42
  "@fastify/static": "^8.0.3",
43
- "path-to-regexp": "^8.1.0"
43
+ "path-to-regexp": "^8.1.0",
44
+ "vite-plugin-node-polyfills": "^0.23.0"
44
45
  },
45
46
  "devDependencies": {
46
47
  "@arethetypeswrong/cli": "^0.15.4",
47
48
  "@babel/preset-typescript": "^7.24.7",
48
49
  "@changesets/cli": "^2.27.7",
49
- "@testing-library/dom": "^10.4.0",
50
- "@testing-library/react": "^16.1.0",
51
50
  "@types/node": "^20.14.9",
52
- "@types/react": "^19.0.2",
53
- "@types/react-dom": "^19.0.2",
54
51
  "@vitest/coverage-v8": "^2.1.0",
55
52
  "fastify": "^5.3.3",
56
53
  "jsdom": "^25.0.0",
57
54
  "prettier": "^3.3.3",
58
- "react-dom": "^19.0.0",
59
55
  "tsup": "^8.2.4",
60
56
  "typescript": "^5.5.4",
61
57
  "vite": "^6.3.5",
@@ -63,8 +59,6 @@
63
59
  },
64
60
  "peerDependencies": {
65
61
  "fastify": "^5.3.3",
66
- "react": "^19.0.0",
67
- "react-dom": "^19.0.0",
68
62
  "typescript": "^5.5.4",
69
63
  "vite": "^6.3.5"
70
64
  },