xpine 0.0.20 → 0.0.21

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/dist/index.js CHANGED
@@ -6,11 +6,10 @@ import EventEmitter from "events";
6
6
  import chokidar from "chokidar";
7
7
 
8
8
  // src/scripts/build.ts
9
- import path3 from "path";
10
- import fs2 from "fs-extra";
9
+ import path5 from "path";
10
+ import fs5 from "fs-extra";
11
11
  import { build } from "esbuild";
12
- import builtinModules from "builtin-modules";
13
- import ts2 from "typescript";
12
+ import ts3 from "typescript";
14
13
 
15
14
  // src/build/typescript-builder.ts
16
15
  import ts from "typescript";
@@ -114,17 +113,6 @@ function removeClientScriptInTSXFile(pathName, source) {
114
113
  clientDataStart
115
114
  };
116
115
  }
117
- function createStaticFile(pathName, source) {
118
- source.forEachChild((child) => {
119
- if (child.kind === ts.SyntaxKind.ExpressionStatement) {
120
- const text = child.getText(source);
121
- const cleanedText = text.replace(/["';]/g, "");
122
- if (cleanedText === "xpine-static") {
123
- console.log("make this file static", pathName);
124
- }
125
- }
126
- });
127
- }
128
116
 
129
117
  // src/scripts/build.ts
130
118
  import { globSync } from "glob";
@@ -181,6 +169,9 @@ var configDefaults = {
181
169
  get pagesDir() {
182
170
  return path2.join(this.srcDir, "./pages");
183
171
  },
172
+ get distPagesDir() {
173
+ return path2.join(this.distDir, "./pages");
174
+ },
184
175
  get publicDir() {
185
176
  return path2.join(this.srcDir, "./public");
186
177
  },
@@ -216,11 +207,117 @@ var plugin = (opts = {}) => {
216
207
  plugin.postcss = true;
217
208
  var remove_layers_default = plugin;
218
209
 
210
+ // src/build/esbuild/transformTSXFiles.ts
211
+ import fs2 from "fs-extra";
212
+ import ts2 from "typescript";
213
+ import path3 from "path";
214
+
215
+ // src/util/regex.ts
216
+ var regex_default = {
217
+ dotTsx: /.tsx/,
218
+ hasLetters: /[A-Za-z0-9]/g,
219
+ configFile: /\+config\.[tj]s/g,
220
+ dynamicRoutes: /(\[)(.*?)(\])/g,
221
+ isDynamicRoute: /\[(.*)\]/g,
222
+ endsWithTSX: /\.tsx$/,
223
+ endsWithJSX: /\.jsx$/
224
+ };
225
+
226
+ // src/build/esbuild/transformTSXFiles.ts
227
+ function transformTSXFiles(componentData, pageConfigFiles) {
228
+ return {
229
+ name: "transform-tsx-files",
230
+ setup(build2) {
231
+ build2.onLoad({ filter: regex_default.dotTsx }, (args) => {
232
+ const configFile = getConfigFile(args.path, pageConfigFiles);
233
+ const content = fs2.readFileSync(args.path, "utf-8");
234
+ const source = ts2.createSourceFile(
235
+ args.path,
236
+ content,
237
+ ts2.ScriptTarget.Latest
238
+ );
239
+ const cleanedContent = removeClientScriptInTSXFile(args.path, source);
240
+ const htmlImportStart = "import { html } from 'xpine';\n";
241
+ const newContent = `${htmlImportStart}${cleanedContent.content}`;
242
+ componentData.push({
243
+ ...args,
244
+ contents: `${htmlImportStart}${cleanedContent.fullContent}`,
245
+ clientContent: cleanedContent.clientContent,
246
+ configFile
247
+ });
248
+ return {
249
+ contents: newContent,
250
+ loader: "tsx"
251
+ };
252
+ });
253
+ }
254
+ };
255
+ }
256
+ function getConfigFile(pathName, pageConfigFiles) {
257
+ const configs = [];
258
+ for (const configFile of pageConfigFiles) {
259
+ const result = path3.relative(pathName, configFile)?.replace(regex_default.configFile, "");
260
+ const hasLetters = result.match(regex_default.hasLetters);
261
+ if (!hasLetters) configs.push(configFile);
262
+ }
263
+ const configToUse = configs.sort((a, b) => b.length - a.length)?.[0];
264
+ return configToUse;
265
+ }
266
+
267
+ // src/build/esbuild/addDotJS.ts
268
+ import fs3 from "fs-extra";
269
+ import path4 from "path";
270
+ import builtinModules from "builtin-modules";
271
+ function addDotJS(allPackages2, extensions2, isDev) {
272
+ const allPackagesIncludingNode = allPackages2.concat(builtinModules);
273
+ return {
274
+ name: "add-dot-js",
275
+ setup(build2) {
276
+ build2.onResolve({ filter: /.*/ }, (args) => {
277
+ const hasAtSign = args.path.startsWith("@");
278
+ const isPackage = hasAtSign ? allPackagesIncludingNode.includes(args.path) : allPackagesIncludingNode.includes(args.path.split("/").shift());
279
+ if (args.importer && !isPackage) {
280
+ const calculatedDir = path4.join(args.resolveDir, args.path);
281
+ let existsAsFile = false;
282
+ for (const extension of extensions2) {
283
+ const asFile = calculatedDir + extension;
284
+ const exists = fs3.existsSync(asFile);
285
+ if (exists) existsAsFile = true;
286
+ }
287
+ let outputPath = args.path + (existsAsFile ? "" : "/index") + ".js";
288
+ outputPath = args.path.endsWith(".js") || args.path.endsWith(".mjs") ? args.path : outputPath;
289
+ return { path: outputPath + (isDev ? `?cache=${Date.now()}` : ""), external: true };
290
+ }
291
+ });
292
+ }
293
+ };
294
+ }
295
+
296
+ // src/build/esbuild/getDataFiles.ts
297
+ import fs4 from "fs-extra";
298
+ function getDataFiles(dataFiles) {
299
+ return {
300
+ name: "get-data-files",
301
+ setup(build2) {
302
+ build2.onLoad({ filter: /\.data\.(js|mjs|ts)$/ }, (args) => {
303
+ const contents = fs4.readFileSync(args.path, "utf-8");
304
+ dataFiles.push({
305
+ ...args,
306
+ contents
307
+ });
308
+ return {
309
+ contents,
310
+ loader: "ts"
311
+ };
312
+ });
313
+ }
314
+ };
315
+ }
316
+
219
317
  // src/scripts/build.ts
220
318
  var extensions = [".ts", ".tsx"];
221
- var packageJson = JSON.parse(fs2.readFileSync(config.packageJsonPath, "utf-8"));
319
+ var packageJson = JSON.parse(fs5.readFileSync(config.packageJsonPath, "utf-8"));
222
320
  var allPackages = Object.keys(packageJson.devDependencies).concat(Object.keys(packageJson.dependencies));
223
- var allPackagesIncludingNode = allPackages.concat(builtinModules);
224
321
  var xpineDistDir = getXPineDistDir();
225
322
  async function buildApp(isDev = false) {
226
323
  try {
@@ -228,9 +325,10 @@ async function buildApp(isDev = false) {
228
325
  const { componentData, dataFiles } = await buildAppFiles(srcDirFiles, isDev);
229
326
  const alpineDataFile = await buildAlpineDataFile(componentData, dataFiles);
230
327
  await buildClientSideFiles([alpineDataFile], isDev);
231
- fs2.removeSync(config.distTempFolder);
328
+ fs5.removeSync(config.distTempFolder);
232
329
  await buildCSS();
233
330
  await buildPublicFolderSymlinks();
331
+ if (!isDev) await buildStaticFiles(componentData);
234
332
  } catch (err) {
235
333
  console.error("Build failed");
236
334
  console.error(err);
@@ -239,8 +337,12 @@ async function buildApp(isDev = false) {
239
337
  async function buildAppFiles(files, isDev) {
240
338
  const componentData = [];
241
339
  const dataFiles = [];
340
+ const pageConfigFiles = files.filter((file) => {
341
+ const fileName = file.split("/").at(-1).split(".").shift();
342
+ return fileName === "+config";
343
+ });
242
344
  const backendFiles = files.filter((file) => extensions.find((ext) => file.endsWith(ext))).filter((file) => !file.startsWith(config.publicDir));
243
- fs2.ensureDirSync(config.distDir);
345
+ fs5.ensureDirSync(config.distDir);
244
346
  await build({
245
347
  entryPoints: backendFiles,
246
348
  format: "esm",
@@ -252,69 +354,9 @@ async function buildAppFiles(files, isDev) {
252
354
  jsx: "transform",
253
355
  minify: !isDev,
254
356
  plugins: [
255
- {
256
- name: "add-dot-js",
257
- setup(build2) {
258
- build2.onResolve({ filter: /.*/ }, (args) => {
259
- const hasAtSign = args.path.startsWith("@");
260
- const isPackage = hasAtSign ? allPackagesIncludingNode.includes(args.path) : allPackagesIncludingNode.includes(args.path.split("/").shift());
261
- if (args.importer && !isPackage) {
262
- const calculatedDir = path3.join(args.resolveDir, args.path);
263
- let existsAsFile = false;
264
- for (const extension of extensions) {
265
- const asFile = calculatedDir + extension;
266
- const exists = fs2.existsSync(asFile);
267
- if (exists) existsAsFile = true;
268
- }
269
- let outputPath = args.path + (existsAsFile ? "" : "/index") + ".js";
270
- outputPath = args.path.endsWith(".js") || args.path.endsWith(".mjs") ? args.path : outputPath;
271
- return { path: outputPath + (isDev ? `?cache=${Date.now()}` : ""), external: true };
272
- }
273
- });
274
- }
275
- },
276
- {
277
- name: "insert-html-banner-and-remove-client-scripts",
278
- setup(build2) {
279
- build2.onLoad({ filter: /.tsx/ }, (args) => {
280
- const content = fs2.readFileSync(args.path, "utf-8");
281
- const source = ts2.createSourceFile(
282
- args.path,
283
- content,
284
- ts2.ScriptTarget.Latest
285
- );
286
- const cleanedContent = removeClientScriptInTSXFile(args.path, source);
287
- createStaticFile(args.path, source);
288
- const htmlImportStart = "import { html } from 'xpine';\n";
289
- const newContent = `${htmlImportStart}${cleanedContent.content}`;
290
- componentData.push({
291
- ...args,
292
- contents: `${htmlImportStart}${cleanedContent.fullContent}`,
293
- clientContent: cleanedContent.clientContent
294
- });
295
- return {
296
- contents: newContent,
297
- loader: "tsx"
298
- };
299
- });
300
- }
301
- },
302
- {
303
- name: "get-data-files",
304
- setup(build2) {
305
- build2.onLoad({ filter: /\.data\.(js|mjs|ts)$/ }, (args) => {
306
- const contents = fs2.readFileSync(args.path, "utf-8");
307
- dataFiles.push({
308
- ...args,
309
- contents
310
- });
311
- return {
312
- contents,
313
- loader: "ts"
314
- };
315
- });
316
- }
317
- }
357
+ addDotJS(allPackages, extensions, isDev),
358
+ transformTSXFiles(componentData, pageConfigFiles),
359
+ getDataFiles(dataFiles)
318
360
  ]
319
361
  });
320
362
  await logSize(config.distDir, "app");
@@ -324,8 +366,8 @@ async function buildAppFiles(files, isDev) {
324
366
  };
325
367
  }
326
368
  async function buildClientSideFiles(alpineDataFiles = [], isDev) {
327
- const tempFilePath = path3.join(config.distTempFolder, "./app.ts");
328
- fs2.ensureFileSync(tempFilePath);
369
+ const tempFilePath = path5.join(config.distTempFolder, "./app.ts");
370
+ fs5.ensureFileSync(tempFilePath);
329
371
  const pagesScriptsGlob = config.publicDir + "/scripts/pages/**/*.{js,ts}";
330
372
  const clientFiles = globSync(
331
373
  config.publicDir + "/**/*.{js,ts}",
@@ -354,15 +396,15 @@ async function buildClientSideFiles(alpineDataFiles = [], isDev) {
354
396
  await logSize(config.distPublicDir, "client");
355
397
  }
356
398
  function writeDevServerClientSideCode(tempFilePath) {
357
- const devServerPath = path3.join(xpineDistDir, "./src/static/dev-server.js");
358
- const content = fs2.readFileSync(devServerPath, "utf-8");
359
- fs2.appendFileSync(tempFilePath, `
399
+ const devServerPath = path5.join(xpineDistDir, "./src/static/dev-server.js");
400
+ const content = fs5.readFileSync(devServerPath, "utf-8");
401
+ fs5.appendFileSync(tempFilePath, `
360
402
  ` + content);
361
403
  }
362
404
  function writeSpaClientSideCode(tempFilePath) {
363
- const spaPath = path3.join(xpineDistDir, "./src/static/spa.js");
364
- const content = fs2.readFileSync(spaPath, "utf-8");
365
- fs2.appendFileSync(tempFilePath, `
405
+ const spaPath = path5.join(xpineDistDir, "./src/static/spa.js");
406
+ const content = fs5.readFileSync(spaPath, "utf-8");
407
+ fs5.appendFileSync(tempFilePath, `
366
408
  ` + content);
367
409
  }
368
410
  async function buildAlpineDataFile(componentData, dataFiles) {
@@ -382,10 +424,10 @@ async function buildAlpineDataFile(componentData, dataFiles) {
382
424
  };
383
425
  const componentsAndDataFiles = componentData.concat(dataFiles);
384
426
  for (const component of componentsAndDataFiles) {
385
- const sourceFile = ts2.createSourceFile(
427
+ const sourceFile = ts3.createSourceFile(
386
428
  component.path,
387
429
  component.contents,
388
- ts2.ScriptTarget.Latest
430
+ ts3.ScriptTarget.Latest
389
431
  );
390
432
  const dataFunctionResult = findDataAttributesAndFunctions(sourceFile, sourceFile);
391
433
  dataFunctionResults.foundDataAttributes.push(...dataFunctionResult.foundDataAttributes);
@@ -407,18 +449,18 @@ async function buildAlpineDataFile(componentData, dataFiles) {
407
449
  output.code.push(`Alpine.data('${dataFunction.name}', ${dataFunction.name});`);
408
450
  }
409
451
  const result = output.imports.join("\n") + "\n" + output.code.join("\n") + "\n" + output.end.join("\n");
410
- fs2.ensureFileSync(config.alpineDataPath);
411
- fs2.writeFileSync(config.alpineDataPath, result);
452
+ fs5.ensureFileSync(config.alpineDataPath);
453
+ fs5.writeFileSync(config.alpineDataPath, result);
412
454
  return config.alpineDataPath;
413
455
  }
414
456
  async function buildCSS() {
415
457
  const cssFiles = globSync(config.srcDir + "/**/*.css");
416
458
  for (const file of cssFiles) {
417
- const fileContents = fs2.readFileSync(file, "utf-8");
459
+ const fileContents = fs5.readFileSync(file, "utf-8");
418
460
  const result = await postcss([tailwindPostcss(), remove_layers_default()]).process(fileContents, { from: file });
419
461
  const newPath = file.replace(config.srcDir, config.distDir);
420
- fs2.ensureFileSync(newPath);
421
- fs2.writeFileSync(newPath, result.css);
462
+ fs5.ensureFileSync(newPath);
463
+ fs5.writeFileSync(newPath, result.css);
422
464
  }
423
465
  logSize(config.distPublicDir, "css");
424
466
  }
@@ -432,8 +474,8 @@ async function buildPublicFolderSymlinks() {
432
474
  const splitNewPath = newPath.split("/");
433
475
  splitNewPath.pop();
434
476
  const newDir = splitNewPath.join("/");
435
- fs2.ensureDirSync(newDir);
436
- fs2.symlinkSync(file, newPath);
477
+ fs5.ensureDirSync(newDir);
478
+ fs5.symlinkSync(file, newPath);
437
479
  } catch {
438
480
  }
439
481
  }
@@ -444,7 +486,7 @@ async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
444
486
  if (!validExtensions.find((ext) => file.endsWith(ext))) return false;
445
487
  return {
446
488
  file,
447
- size: fs2.statSync(file).size / (1024 * 1e3)
489
+ size: fs5.statSync(file).size / (1024 * 1e3)
448
490
  };
449
491
  }).filter(Boolean);
450
492
  const totalSize = fileSizes.reduce((total, current) => {
@@ -452,17 +494,75 @@ async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
452
494
  }, 0);
453
495
  console.info(`[${totalSize.toFixed(3)} MB] Built ${type}`);
454
496
  }
497
+ async function buildStaticFiles(componentData) {
498
+ const componentsWithConfigs = componentData.filter((item) => item.configFile);
499
+ for (const component of componentsWithConfigs) {
500
+ const config2 = (await import(sourcePathToDistPath(component.configFile) + `?cache=${Date.now()}`)).default;
501
+ const shouldBeStatic = config2?.staticPaths;
502
+ if (shouldBeStatic) {
503
+ let componentFileName = component.path.split("/").pop().replace(regex_default.endsWithJSX, "").replace(regex_default.endsWithTSX, "");
504
+ const isDynamicRoute = component.path.match(regex_default.isDynamicRoute);
505
+ if (isDynamicRoute) {
506
+ componentFileName = component.path.split("/").filter((dir) => dir.match(regex_default.isDynamicRoute)).join("/").replace(regex_default.endsWithJSX, "").replace(regex_default.endsWithTSX, "");
507
+ }
508
+ const builtComponentPath = sourcePathToDistPath(component.path);
509
+ const componentDynamicPaths = getComponentDynamicPaths(componentFileName);
510
+ const componentFn = (await import(builtComponentPath + `?cache=${Date.now()}`)).default;
511
+ const outputPath = componentDynamicPaths?.length ? componentDynamicPaths.reduce((total, current) => {
512
+ return total.replace(`/[${current}]`, "");
513
+ }, path5.dirname(builtComponentPath)) : path5.dirname(builtComponentPath);
514
+ if (typeof shouldBeStatic === "boolean") {
515
+ try {
516
+ const staticComponentOutput = await componentFn();
517
+ fs5.writeFileSync(path5.join(outputPath, "./index.html"), staticComponentOutput);
518
+ } catch (err) {
519
+ console.error(err);
520
+ console.log("Could not build static component", component.path);
521
+ }
522
+ } else if (typeof shouldBeStatic === "function") {
523
+ const dynamicPaths = await shouldBeStatic();
524
+ for (const dynamicPath of dynamicPaths) {
525
+ try {
526
+ const staticComponentOutput = await componentFn({
527
+ params: {
528
+ ...componentDynamicPaths?.length ? dynamicPath : {}
529
+ }
530
+ });
531
+ const updatedOutDir = path5.join(outputPath, `./${componentDynamicPaths.map((key) => dynamicPath[key]).join("/")}`);
532
+ fs5.ensureDirSync(updatedOutDir);
533
+ fs5.writeFileSync(path5.join(updatedOutDir, `./index.html`), staticComponentOutput);
534
+ } catch (err) {
535
+ console.log("Could not build static component", component.path);
536
+ console.error(err);
537
+ }
538
+ }
539
+ }
540
+ }
541
+ }
542
+ }
543
+ function sourcePathToDistPath(sourcePath) {
544
+ return sourcePath.replace(config.srcDir, config.distDir).replace(/\.ts$/, ".js").replace(/\.tsx$/, ".js");
545
+ }
546
+ function getComponentDynamicPaths(componentPath) {
547
+ const matches = [...componentPath.matchAll(regex_default.dynamicRoutes)];
548
+ if (!matches?.length) return null;
549
+ const output = [];
550
+ for (const match of matches) {
551
+ output.push(match[2]);
552
+ }
553
+ return output;
554
+ }
455
555
 
456
556
  // src/runDevServer.ts
457
- import path5 from "path";
557
+ import path7 from "path";
458
558
 
459
559
  // src/util/env.ts
460
560
  import dotenv from "dotenv";
461
- import path4 from "path";
561
+ import path6 from "path";
462
562
  import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
463
563
  async function setupEnv() {
464
564
  if (process.env.HAS_SETUP_ENV) return;
465
- dotenv.config({ path: path4.join(config.rootDir, `./.env.${process.env.STAGE || "dev"}`) });
565
+ dotenv.config({ path: path6.join(config.rootDir, `./.env.${process.env.STAGE || "dev"}`) });
466
566
  await loadSecretsManagerSecrets();
467
567
  process.env.HAS_SETUP_ENV = "true";
468
568
  }
@@ -491,16 +591,16 @@ async function loadSecretsManagerSecrets() {
491
591
  await setupEnv();
492
592
  async function runDevServer() {
493
593
  process.env.NODE_ENV = "development";
494
- const startServer = (await import(config.serverDistAppPath + `?cache=${Date.now()}`)).default;
495
594
  await buildApp(true);
595
+ const startServer = (await import(config.serverDistAppPath + `?cache=${Date.now()}`)).default;
496
596
  let appServer = await startServer();
497
597
  const watcher = chokidar.watch(config.srcDir, {
498
598
  ignoreInitial: true,
499
599
  // Ignore map and prisma files
500
- ignored: (pathName) => pathName.endsWith(".map") || pathName.startsWith(path5.join(config.serverDir, "./prisma"))
600
+ ignored: (pathName) => pathName.endsWith(".map") || pathName.startsWith(path7.join(config.serverDir, "./prisma"))
501
601
  });
502
- watcher.on("all", async (event, path6) => {
503
- const shouldReloadServer = path6.startsWith(config.serverDir) && !path6.startsWith(config.runDir) || ["add", "unlink"].includes(event) && path6.startsWith(config.pagesDir);
602
+ watcher.on("all", async (event, path9) => {
603
+ const shouldReloadServer = path9.startsWith(config.serverDir) && !path9.startsWith(config.runDir) || ["add", "unlink"].includes(event) && path9.startsWith(config.pagesDir);
504
604
  if (shouldReloadServer) {
505
605
  await appServer.server.close();
506
606
  await buildApp(true);
@@ -574,29 +674,34 @@ function getTokenFromRequest(req) {
574
674
 
575
675
  // src/express.ts
576
676
  import requestIP from "request-ip";
677
+ import fs6 from "fs-extra";
678
+ import path8 from "path";
577
679
  var doctypeHTML = "<!DOCTYPE html>";
578
680
  async function createRouter() {
579
681
  const methods = ["get", "post", "put", "patch", "delete"];
580
682
  const router = express2.Router();
581
683
  const routes = globSync2(config.pagesDir + "/**/*.{tsx,ts}");
582
684
  const routeMap = routes.map((route) => {
685
+ const routeFormatted = route.split(config.pagesDir).pop().replace(".tsx", "").replace(".js", "").replace(".ts", "");
686
+ if (routeFormatted.endsWith("+config")) return;
687
+ const routeFormattedWithIndex = routeFormatted.replace(/\/index$/g, "");
583
688
  return {
584
- route: route.split(config.pagesDir).pop().replace(".tsx", "").replace(".js", "").replace(".ts", "").replace("/index", "/"),
689
+ route: routeFormattedWithIndex,
585
690
  path: route.replace(config.srcDir, config.distDir).replace(".tsx", ".js").replace(".ts", ".js"),
586
691
  originalRoute: route
587
692
  };
588
- });
693
+ }).filter(Boolean);
589
694
  const routeResults = [];
590
695
  for (const route of routeMap) {
591
696
  const isJSX = route.originalRoute.endsWith(".tsx") || route.originalRoute.endsWith(".jsx");
592
697
  const routeItem = process.env.NODE_ENV === "development" ? null : (await import(route.path)).default;
593
698
  const slugRoute = route.route.toLowerCase().replace(/[ ]/g, "");
594
699
  const foundMethod = methods.find((method) => slugRoute.endsWith(`.${method}`));
595
- const isDynamicRoute = slugRoute.match(/\[(.*)\]/g);
700
+ const isDynamicRoute = slugRoute.match(regex_default.isDynamicRoute);
596
701
  let formattedRouteItem = slugRoute;
597
702
  if (foundMethod) formattedRouteItem = formattedRouteItem.split(".").shift();
598
703
  if (isDynamicRoute) {
599
- const result = [...formattedRouteItem.matchAll(/(\[)(.*?)(\])/g)];
704
+ const result = [...formattedRouteItem.matchAll(regex_default.dynamicRoutes)];
600
705
  for (const match of result) {
601
706
  formattedRouteItem = formattedRouteItem.replace(match[0], ":" + match[2]);
602
707
  }
@@ -608,6 +713,11 @@ async function createRouter() {
608
713
  });
609
714
  router[foundMethod || "get"](formattedRouteItem, async (req, res) => {
610
715
  try {
716
+ const staticPath = routeHasStaticPath(formattedRouteItem, req.params);
717
+ if (staticPath && process.env.NODE_ENV !== "development") {
718
+ res.sendFile(staticPath);
719
+ return;
720
+ }
611
721
  if (routeItem) {
612
722
  if (isJSX) {
613
723
  res.send(doctypeHTML + await routeItem(req, res));
@@ -669,6 +779,17 @@ async function createXPineRouter(app, beforeErrorRoute) {
669
779
  }
670
780
  });
671
781
  }
782
+ function routeHasStaticPath(route, params) {
783
+ const paramEntries = Object.entries(params);
784
+ let routeToStaticPath = route;
785
+ for (const [key, value] of paramEntries) {
786
+ routeToStaticPath = routeToStaticPath.replace(`:${key}`, value);
787
+ }
788
+ routeToStaticPath += "/index.html";
789
+ const outputPath = path8.join(config.distPagesDir, routeToStaticPath);
790
+ if (fs6.existsSync(outputPath)) return outputPath;
791
+ return false;
792
+ }
672
793
 
673
794
  // src/util/html.ts
674
795
  var html = class {
@@ -700,15 +821,19 @@ export {
700
821
  buildApp,
701
822
  buildCSS,
702
823
  buildPublicFolderSymlinks,
824
+ buildStaticFiles,
703
825
  config,
704
826
  createRouter,
705
827
  createXPineRouter,
706
828
  fromRoot,
829
+ getComponentDynamicPaths,
707
830
  getTokenFromRequest,
708
831
  html,
709
832
  logSize,
833
+ routeHasStaticPath,
710
834
  runDevServer,
711
835
  setupEnv,
712
836
  signUser,
837
+ sourcePathToDistPath,
713
838
  verifyUser
714
839
  };