xpine 0.0.21 → 0.0.24

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 (40) hide show
  1. package/TODO +0 -2
  2. package/dist/index.js +184 -80
  3. package/dist/src/build/esbuild/addDotJS.d.ts +5 -0
  4. package/dist/src/build/esbuild/addDotJS.d.ts.map +1 -0
  5. package/dist/src/build/esbuild/getDataFiles.d.ts +5 -0
  6. package/dist/src/build/esbuild/getDataFiles.d.ts.map +1 -0
  7. package/dist/src/build/esbuild/transformTSXFiles.d.ts +6 -0
  8. package/dist/src/build/esbuild/transformTSXFiles.d.ts.map +1 -0
  9. package/dist/src/express.d.ts +3 -0
  10. package/dist/src/express.d.ts.map +1 -1
  11. package/dist/src/runDevServer.d.ts.map +1 -1
  12. package/dist/src/scripts/build.d.ts +5 -1
  13. package/dist/src/scripts/build.d.ts.map +1 -1
  14. package/dist/src/scripts/xpine-build.js +110 -65
  15. package/dist/src/scripts/xpine-dev.js +148 -68
  16. package/dist/src/static/spa.js +2 -1
  17. package/dist/src/util/config-file.d.ts +6 -0
  18. package/dist/src/util/config-file.d.ts.map +1 -0
  19. package/dist/src/util/constants.d.ts +3 -0
  20. package/dist/src/util/constants.d.ts.map +1 -0
  21. package/dist/src/util/get-config.d.ts.map +1 -1
  22. package/dist/src/util/postcss/remove-layers.d.ts +1 -1
  23. package/dist/src/util/regex.d.ts +11 -0
  24. package/dist/src/util/regex.d.ts.map +1 -0
  25. package/dist/src/util/require.d.ts.map +1 -1
  26. package/dist/types.d.ts +31 -2
  27. package/dist/types.d.ts.map +1 -1
  28. package/eslint.config.mjs +27 -0
  29. package/package.json +18 -4
  30. package/tests/e2e/alpine.spec.ts +14 -0
  31. package/tests/e2e/app-build.spec.ts +73 -0
  32. package/tests/e2e/spa.spec.ts +23 -0
  33. package/tests/e2e/tests-examples/demo-todo-app.spec.ts +437 -0
  34. package/tests/eslint.config.mjs +27 -0
  35. package/tests/package-lock.json +8941 -0
  36. package/tests/package.json +89 -0
  37. package/tests/playwright.config.ts +81 -0
  38. package/tests/tsconfig.json +46 -0
  39. package/tests/xpine.config.mjs +1 -0
  40. package/types.ts +34 -2
@@ -123,6 +123,10 @@ var require_default = createRequire(import.meta.url);
123
123
  function getXPineDistDir() {
124
124
  const dir = import.meta.dirname;
125
125
  const splitDir = dir.split("/xpine/dist");
126
+ if (splitDir.length === 1) {
127
+ const splitDirSingle = dir.split("/xpine/");
128
+ return splitDirSingle[0] + "/xpine/dist";
129
+ }
126
130
  return splitDir[0] + "/xpine/dist";
127
131
  }
128
132
 
@@ -205,26 +209,55 @@ var remove_layers_default = plugin;
205
209
  // src/build/esbuild/transformTSXFiles.ts
206
210
  import fs2 from "fs-extra";
207
211
  import ts2 from "typescript";
208
- import path3 from "path";
209
212
 
210
213
  // src/util/regex.ts
211
214
  var regex_default = {
212
215
  dotTsx: /.tsx/,
213
216
  hasLetters: /[A-Za-z0-9]/g,
214
- configFile: /\+config\.[tj]s/g,
217
+ configFile: /\+config\.[tj]sx?/g,
215
218
  dynamicRoutes: /(\[)(.*?)(\])/g,
216
219
  isDynamicRoute: /\[(.*)\]/g,
217
220
  endsWithTSX: /\.tsx$/,
218
221
  endsWithJSX: /\.jsx$/
219
222
  };
220
223
 
224
+ // src/util/config-file.ts
225
+ import path3 from "path";
226
+ function sourcePathToDistPath(sourcePath) {
227
+ return sourcePath?.replace(config.srcDir, config.distDir)?.replace(/\.ts$/, ".js")?.replace(/\.tsx$/, ".js");
228
+ }
229
+ function getConfigFiles(pathName, pageConfigFiles, returnDistPaths = true) {
230
+ const configs = [];
231
+ for (const configFile of pageConfigFiles) {
232
+ const result = path3.relative(pathName, configFile)?.replace(regex_default.configFile, "");
233
+ const hasLetters = result.match(regex_default.hasLetters);
234
+ if (!hasLetters) {
235
+ configs.push(returnDistPaths ? sourcePathToDistPath(configFile) : configFile);
236
+ }
237
+ }
238
+ if (!configs.length) return null;
239
+ return configs.sort((a, b) => b.length - a.length);
240
+ }
241
+ async function getCompleteConfig(configFiles, cacheKey) {
242
+ let config2 = {};
243
+ for (const configFile of configFiles) {
244
+ config2 = {
245
+ ...(await import(configFile + `?cache=${cacheKey}`)).default,
246
+ ...config2
247
+ };
248
+ }
249
+ return config2;
250
+ }
251
+ function isAConfigFile(pathName) {
252
+ return !!pathName.match(regex_default.configFile);
253
+ }
254
+
221
255
  // src/build/esbuild/transformTSXFiles.ts
222
256
  function transformTSXFiles(componentData, pageConfigFiles) {
223
257
  return {
224
258
  name: "transform-tsx-files",
225
259
  setup(build2) {
226
- build2.onLoad({ filter: regex_default.dotTsx }, (args) => {
227
- const configFile = getConfigFile(args.path, pageConfigFiles);
260
+ build2.onLoad({ filter: regex_default.dotTsx }, async (args) => {
228
261
  const content = fs2.readFileSync(args.path, "utf-8");
229
262
  const source = ts2.createSourceFile(
230
263
  args.path,
@@ -238,7 +271,7 @@ function transformTSXFiles(componentData, pageConfigFiles) {
238
271
  ...args,
239
272
  contents: `${htmlImportStart}${cleanedContent.fullContent}`,
240
273
  clientContent: cleanedContent.clientContent,
241
- configFile
274
+ configFiles: isAConfigFile(args?.path) ? null : getConfigFiles(args.path, pageConfigFiles)
242
275
  });
243
276
  return {
244
277
  contents: newContent,
@@ -248,16 +281,6 @@ function transformTSXFiles(componentData, pageConfigFiles) {
248
281
  }
249
282
  };
250
283
  }
251
- function getConfigFile(pathName, pageConfigFiles) {
252
- const configs = [];
253
- for (const configFile of pageConfigFiles) {
254
- const result = path3.relative(pathName, configFile)?.replace(regex_default.configFile, "");
255
- const hasLetters = result.match(regex_default.hasLetters);
256
- if (!hasLetters) configs.push(configFile);
257
- }
258
- const configToUse = configs.sort((a, b) => b.length - a.length)?.[0];
259
- return configToUse;
260
- }
261
284
 
262
285
  // src/build/esbuild/addDotJS.ts
263
286
  import fs3 from "fs-extra";
@@ -309,13 +332,18 @@ function getDataFiles(dataFiles) {
309
332
  };
310
333
  }
311
334
 
335
+ // src/util/constants.ts
336
+ var doctypeHTML = "<!DOCTYPE html>";
337
+ var staticComment = "<!-- static -->";
338
+
312
339
  // src/scripts/build.ts
313
340
  var extensions = [".ts", ".tsx"];
314
341
  var packageJson = JSON.parse(fs5.readFileSync(config.packageJsonPath, "utf-8"));
315
342
  var allPackages = Object.keys(packageJson.devDependencies).concat(Object.keys(packageJson.dependencies));
316
343
  var xpineDistDir = getXPineDistDir();
317
- async function buildApp(isDev = false) {
344
+ async function buildApp(isDev = false, removePreviousBuild = false) {
318
345
  try {
346
+ if (removePreviousBuild) fs5.removeSync(config.distDir);
319
347
  const srcDirFiles = globSync(config.srcDir + "/**/*.{js,ts,tsx,jsx}");
320
348
  const { componentData, dataFiles } = await buildAppFiles(srcDirFiles, isDev);
321
349
  const alpineDataFile = await buildAlpineDataFile(componentData, dataFiles);
@@ -323,7 +351,7 @@ async function buildApp(isDev = false) {
323
351
  fs5.removeSync(config.distTempFolder);
324
352
  await buildCSS();
325
353
  await buildPublicFolderSymlinks();
326
- if (!isDev) await buildStaticFiles(componentData);
354
+ if (!isDev) await buildFilesWithConfigs(componentData);
327
355
  } catch (err) {
328
356
  console.error("Build failed");
329
357
  console.error(err);
@@ -393,14 +421,12 @@ async function buildClientSideFiles(alpineDataFiles = [], isDev) {
393
421
  function writeDevServerClientSideCode(tempFilePath) {
394
422
  const devServerPath = path5.join(xpineDistDir, "./src/static/dev-server.js");
395
423
  const content = fs5.readFileSync(devServerPath, "utf-8");
396
- fs5.appendFileSync(tempFilePath, `
397
- ` + content);
424
+ fs5.appendFileSync(tempFilePath, "\n" + content);
398
425
  }
399
426
  function writeSpaClientSideCode(tempFilePath) {
400
427
  const spaPath = path5.join(xpineDistDir, "./src/static/spa.js");
401
428
  const content = fs5.readFileSync(spaPath, "utf-8");
402
- fs5.appendFileSync(tempFilePath, `
403
- ` + content);
429
+ fs5.appendFileSync(tempFilePath, "\n" + content);
404
430
  }
405
431
  async function buildAlpineDataFile(componentData, dataFiles) {
406
432
  const output = {
@@ -489,55 +515,71 @@ async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
489
515
  }, 0);
490
516
  console.info(`[${totalSize.toFixed(3)} MB] Built ${type}`);
491
517
  }
492
- async function buildStaticFiles(componentData) {
493
- const componentsWithConfigs = componentData.filter((item) => item.configFile);
518
+ async function buildFilesWithConfigs(componentData) {
519
+ const now = Date.now();
520
+ const componentsWithConfigs = componentData.filter((item) => item.configFiles);
494
521
  for (const component of componentsWithConfigs) {
495
- const config2 = (await import(sourcePathToDistPath(component.configFile) + `?cache=${Date.now()}`)).default;
496
- const shouldBeStatic = config2?.staticPaths;
497
- if (shouldBeStatic) {
498
- let componentFileName = component.path.split("/").pop().replace(regex_default.endsWithJSX, "").replace(regex_default.endsWithTSX, "");
499
- const isDynamicRoute = component.path.match(regex_default.isDynamicRoute);
500
- if (isDynamicRoute) {
501
- componentFileName = component.path.split("/").filter((dir) => dir.match(regex_default.isDynamicRoute)).join("/").replace(regex_default.endsWithJSX, "").replace(regex_default.endsWithTSX, "");
502
- }
503
- const builtComponentPath = sourcePathToDistPath(component.path);
504
- const componentDynamicPaths = getComponentDynamicPaths(componentFileName);
505
- const componentFn = (await import(builtComponentPath + `?cache=${Date.now()}`)).default;
506
- const outputPath = componentDynamicPaths?.length ? componentDynamicPaths.reduce((total, current) => {
507
- return total.replace(`/[${current}]`, "");
508
- }, path5.dirname(builtComponentPath)) : path5.dirname(builtComponentPath);
509
- if (typeof shouldBeStatic === "boolean") {
510
- try {
511
- const staticComponentOutput = await componentFn();
512
- fs5.writeFileSync(path5.join(outputPath, "./index.html"), staticComponentOutput);
513
- } catch (err) {
514
- console.error(err);
515
- console.log("Could not build static component", component.path);
516
- }
517
- } else if (typeof shouldBeStatic === "function") {
518
- const dynamicPaths = await shouldBeStatic();
519
- for (const dynamicPath of dynamicPaths) {
520
- try {
521
- const staticComponentOutput = await componentFn({
522
- params: {
523
- ...componentDynamicPaths?.length ? dynamicPath : {}
524
- }
525
- });
526
- const updatedOutDir = path5.join(outputPath, `./${componentDynamicPaths.map((key) => dynamicPath[key]).join("/")}`);
527
- fs5.ensureDirSync(updatedOutDir);
528
- fs5.writeFileSync(path5.join(updatedOutDir, `./index.html`), staticComponentOutput);
529
- } catch (err) {
530
- console.log("Could not build static component", component.path);
531
- console.error(err);
522
+ let config2 = await getCompleteConfig(component.configFiles, now);
523
+ const builtComponentPath = sourcePathToDistPath(component.path);
524
+ const componentImport = await import(builtComponentPath + `?cache=${Date.now()}`);
525
+ if (componentImport?.config) {
526
+ config2 = {
527
+ ...config2,
528
+ ...componentImport.config
529
+ };
530
+ }
531
+ if (config2?.staticPaths) buildStaticFiles(config2, component, componentImport, builtComponentPath);
532
+ }
533
+ }
534
+ async function buildStaticFiles(config2, component, componentImport, builtComponentPath) {
535
+ if (!config2?.staticPaths) return;
536
+ let componentFileName = component.path.split("/").pop().replace(regex_default.endsWithJSX, "").replace(regex_default.endsWithTSX, "");
537
+ const isDynamicRoute = component.path.match(regex_default.isDynamicRoute);
538
+ if (isDynamicRoute) {
539
+ componentFileName = component.path.split("/").filter((dir) => dir.match(regex_default.isDynamicRoute)).join("/").replace(regex_default.endsWithJSX, "").replace(regex_default.endsWithTSX, "");
540
+ }
541
+ const componentDynamicPaths = getComponentDynamicPaths(componentFileName);
542
+ const componentFn = componentImport.default;
543
+ const outputPath = componentDynamicPaths?.length ? componentDynamicPaths.reduce((total, current) => {
544
+ return total.replace(`/[${current}]`, "");
545
+ }, path5.dirname(builtComponentPath)) : path5.dirname(builtComponentPath);
546
+ if (typeof config2?.staticPaths === "boolean") {
547
+ try {
548
+ const req = { params: {} };
549
+ const data = config2?.data ? await config2.data(req) : null;
550
+ const staticComponentOutput = await componentFn({ data });
551
+ fs5.writeFileSync(
552
+ path5.join(outputPath, "./index.html"),
553
+ doctypeHTML + (config2?.wrapper ? await config2.wrapper({ req, children: staticComponentOutput, config: config2, data }) : staticComponentOutput) + staticComment
554
+ );
555
+ } catch (err) {
556
+ console.error(err);
557
+ console.log("Could not build static component", component.path);
558
+ }
559
+ } else if (typeof config2?.staticPaths === "function") {
560
+ const dynamicPaths = await config2.staticPaths();
561
+ for (const dynamicPath of dynamicPaths) {
562
+ try {
563
+ const req = {
564
+ params: {
565
+ ...componentDynamicPaths?.length ? dynamicPath : {}
532
566
  }
533
- }
567
+ };
568
+ const data = config2?.data ? await config2.data(req) : null;
569
+ const staticComponentOutput = await componentFn({ req, data });
570
+ const updatedOutDir = path5.join(outputPath, `./${componentDynamicPaths.map((key) => dynamicPath[key]).join("/")}`);
571
+ fs5.ensureDirSync(updatedOutDir);
572
+ fs5.writeFileSync(
573
+ path5.join(updatedOutDir, "./index.html"),
574
+ doctypeHTML + (config2?.wrapper ? await config2.wrapper({ req, children: staticComponentOutput, config: config2, data }) : staticComponentOutput) + staticComment
575
+ );
576
+ } catch (err) {
577
+ console.log("Could not build static component", component.path);
578
+ console.error(err);
534
579
  }
535
580
  }
536
581
  }
537
582
  }
538
- function sourcePathToDistPath(sourcePath) {
539
- return sourcePath.replace(config.srcDir, config.distDir).replace(/\.ts$/, ".js").replace(/\.tsx$/, ".js");
540
- }
541
583
  function getComponentDynamicPaths(componentPath) {
542
584
  const matches = [...componentPath.matchAll(regex_default.dynamicRoutes)];
543
585
  if (!matches?.length) return null;
@@ -549,4 +591,7 @@ function getComponentDynamicPaths(componentPath) {
549
591
  }
550
592
 
551
593
  // src/scripts/xpine-build.ts
552
- buildApp();
594
+ import yargs from "yargs";
595
+ import { hideBin } from "yargs/helpers";
596
+ var argv = yargs(hideBin(process.argv)).argv;
597
+ buildApp(argv?.isDev || false, argv?.removePreviousBuild || false);
@@ -130,6 +130,10 @@ var require_default = createRequire(import.meta.url);
130
130
  function getXPineDistDir() {
131
131
  const dir = import.meta.dirname;
132
132
  const splitDir = dir.split("/xpine/dist");
133
+ if (splitDir.length === 1) {
134
+ const splitDirSingle = dir.split("/xpine/");
135
+ return splitDirSingle[0] + "/xpine/dist";
136
+ }
133
137
  return splitDir[0] + "/xpine/dist";
134
138
  }
135
139
 
@@ -212,26 +216,55 @@ var remove_layers_default = plugin;
212
216
  // src/build/esbuild/transformTSXFiles.ts
213
217
  import fs2 from "fs-extra";
214
218
  import ts2 from "typescript";
215
- import path3 from "path";
216
219
 
217
220
  // src/util/regex.ts
218
221
  var regex_default = {
219
222
  dotTsx: /.tsx/,
220
223
  hasLetters: /[A-Za-z0-9]/g,
221
- configFile: /\+config\.[tj]s/g,
224
+ configFile: /\+config\.[tj]sx?/g,
222
225
  dynamicRoutes: /(\[)(.*?)(\])/g,
223
226
  isDynamicRoute: /\[(.*)\]/g,
224
227
  endsWithTSX: /\.tsx$/,
225
228
  endsWithJSX: /\.jsx$/
226
229
  };
227
230
 
231
+ // src/util/config-file.ts
232
+ import path3 from "path";
233
+ function sourcePathToDistPath(sourcePath) {
234
+ return sourcePath?.replace(config.srcDir, config.distDir)?.replace(/\.ts$/, ".js")?.replace(/\.tsx$/, ".js");
235
+ }
236
+ function getConfigFiles(pathName, pageConfigFiles, returnDistPaths = true) {
237
+ const configs = [];
238
+ for (const configFile of pageConfigFiles) {
239
+ const result = path3.relative(pathName, configFile)?.replace(regex_default.configFile, "");
240
+ const hasLetters = result.match(regex_default.hasLetters);
241
+ if (!hasLetters) {
242
+ configs.push(returnDistPaths ? sourcePathToDistPath(configFile) : configFile);
243
+ }
244
+ }
245
+ if (!configs.length) return null;
246
+ return configs.sort((a, b) => b.length - a.length);
247
+ }
248
+ async function getCompleteConfig(configFiles, cacheKey) {
249
+ let config2 = {};
250
+ for (const configFile of configFiles) {
251
+ config2 = {
252
+ ...(await import(configFile + `?cache=${cacheKey}`)).default,
253
+ ...config2
254
+ };
255
+ }
256
+ return config2;
257
+ }
258
+ function isAConfigFile(pathName) {
259
+ return !!pathName.match(regex_default.configFile);
260
+ }
261
+
228
262
  // src/build/esbuild/transformTSXFiles.ts
229
263
  function transformTSXFiles(componentData, pageConfigFiles) {
230
264
  return {
231
265
  name: "transform-tsx-files",
232
266
  setup(build2) {
233
- build2.onLoad({ filter: regex_default.dotTsx }, (args) => {
234
- const configFile = getConfigFile(args.path, pageConfigFiles);
267
+ build2.onLoad({ filter: regex_default.dotTsx }, async (args) => {
235
268
  const content = fs2.readFileSync(args.path, "utf-8");
236
269
  const source = ts2.createSourceFile(
237
270
  args.path,
@@ -245,7 +278,7 @@ function transformTSXFiles(componentData, pageConfigFiles) {
245
278
  ...args,
246
279
  contents: `${htmlImportStart}${cleanedContent.fullContent}`,
247
280
  clientContent: cleanedContent.clientContent,
248
- configFile
281
+ configFiles: isAConfigFile(args?.path) ? null : getConfigFiles(args.path, pageConfigFiles)
249
282
  });
250
283
  return {
251
284
  contents: newContent,
@@ -255,16 +288,6 @@ function transformTSXFiles(componentData, pageConfigFiles) {
255
288
  }
256
289
  };
257
290
  }
258
- function getConfigFile(pathName, pageConfigFiles) {
259
- const configs = [];
260
- for (const configFile of pageConfigFiles) {
261
- const result = path3.relative(pathName, configFile)?.replace(regex_default.configFile, "");
262
- const hasLetters = result.match(regex_default.hasLetters);
263
- if (!hasLetters) configs.push(configFile);
264
- }
265
- const configToUse = configs.sort((a, b) => b.length - a.length)?.[0];
266
- return configToUse;
267
- }
268
291
 
269
292
  // src/build/esbuild/addDotJS.ts
270
293
  import fs3 from "fs-extra";
@@ -316,13 +339,18 @@ function getDataFiles(dataFiles) {
316
339
  };
317
340
  }
318
341
 
342
+ // src/util/constants.ts
343
+ var doctypeHTML = "<!DOCTYPE html>";
344
+ var staticComment = "<!-- static -->";
345
+
319
346
  // src/scripts/build.ts
320
347
  var extensions = [".ts", ".tsx"];
321
348
  var packageJson = JSON.parse(fs5.readFileSync(config.packageJsonPath, "utf-8"));
322
349
  var allPackages = Object.keys(packageJson.devDependencies).concat(Object.keys(packageJson.dependencies));
323
350
  var xpineDistDir = getXPineDistDir();
324
- async function buildApp(isDev = false) {
351
+ async function buildApp(isDev = false, removePreviousBuild = false) {
325
352
  try {
353
+ if (removePreviousBuild) fs5.removeSync(config.distDir);
326
354
  const srcDirFiles = globSync(config.srcDir + "/**/*.{js,ts,tsx,jsx}");
327
355
  const { componentData, dataFiles } = await buildAppFiles(srcDirFiles, isDev);
328
356
  const alpineDataFile = await buildAlpineDataFile(componentData, dataFiles);
@@ -330,7 +358,7 @@ async function buildApp(isDev = false) {
330
358
  fs5.removeSync(config.distTempFolder);
331
359
  await buildCSS();
332
360
  await buildPublicFolderSymlinks();
333
- if (!isDev) await buildStaticFiles(componentData);
361
+ if (!isDev) await buildFilesWithConfigs(componentData);
334
362
  } catch (err) {
335
363
  console.error("Build failed");
336
364
  console.error(err);
@@ -400,14 +428,12 @@ async function buildClientSideFiles(alpineDataFiles = [], isDev) {
400
428
  function writeDevServerClientSideCode(tempFilePath) {
401
429
  const devServerPath = path5.join(xpineDistDir, "./src/static/dev-server.js");
402
430
  const content = fs5.readFileSync(devServerPath, "utf-8");
403
- fs5.appendFileSync(tempFilePath, `
404
- ` + content);
431
+ fs5.appendFileSync(tempFilePath, "\n" + content);
405
432
  }
406
433
  function writeSpaClientSideCode(tempFilePath) {
407
434
  const spaPath = path5.join(xpineDistDir, "./src/static/spa.js");
408
435
  const content = fs5.readFileSync(spaPath, "utf-8");
409
- fs5.appendFileSync(tempFilePath, `
410
- ` + content);
436
+ fs5.appendFileSync(tempFilePath, "\n" + content);
411
437
  }
412
438
  async function buildAlpineDataFile(componentData, dataFiles) {
413
439
  const output = {
@@ -496,55 +522,71 @@ async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
496
522
  }, 0);
497
523
  console.info(`[${totalSize.toFixed(3)} MB] Built ${type}`);
498
524
  }
499
- async function buildStaticFiles(componentData) {
500
- const componentsWithConfigs = componentData.filter((item) => item.configFile);
525
+ async function buildFilesWithConfigs(componentData) {
526
+ const now = Date.now();
527
+ const componentsWithConfigs = componentData.filter((item) => item.configFiles);
501
528
  for (const component of componentsWithConfigs) {
502
- const config2 = (await import(sourcePathToDistPath(component.configFile) + `?cache=${Date.now()}`)).default;
503
- const shouldBeStatic = config2?.staticPaths;
504
- if (shouldBeStatic) {
505
- let componentFileName = component.path.split("/").pop().replace(regex_default.endsWithJSX, "").replace(regex_default.endsWithTSX, "");
506
- const isDynamicRoute = component.path.match(regex_default.isDynamicRoute);
507
- if (isDynamicRoute) {
508
- componentFileName = component.path.split("/").filter((dir) => dir.match(regex_default.isDynamicRoute)).join("/").replace(regex_default.endsWithJSX, "").replace(regex_default.endsWithTSX, "");
509
- }
510
- const builtComponentPath = sourcePathToDistPath(component.path);
511
- const componentDynamicPaths = getComponentDynamicPaths(componentFileName);
512
- const componentFn = (await import(builtComponentPath + `?cache=${Date.now()}`)).default;
513
- const outputPath = componentDynamicPaths?.length ? componentDynamicPaths.reduce((total, current) => {
514
- return total.replace(`/[${current}]`, "");
515
- }, path5.dirname(builtComponentPath)) : path5.dirname(builtComponentPath);
516
- if (typeof shouldBeStatic === "boolean") {
517
- try {
518
- const staticComponentOutput = await componentFn();
519
- fs5.writeFileSync(path5.join(outputPath, "./index.html"), staticComponentOutput);
520
- } catch (err) {
521
- console.error(err);
522
- console.log("Could not build static component", component.path);
523
- }
524
- } else if (typeof shouldBeStatic === "function") {
525
- const dynamicPaths = await shouldBeStatic();
526
- for (const dynamicPath of dynamicPaths) {
527
- try {
528
- const staticComponentOutput = await componentFn({
529
- params: {
530
- ...componentDynamicPaths?.length ? dynamicPath : {}
531
- }
532
- });
533
- const updatedOutDir = path5.join(outputPath, `./${componentDynamicPaths.map((key) => dynamicPath[key]).join("/")}`);
534
- fs5.ensureDirSync(updatedOutDir);
535
- fs5.writeFileSync(path5.join(updatedOutDir, `./index.html`), staticComponentOutput);
536
- } catch (err) {
537
- console.log("Could not build static component", component.path);
538
- console.error(err);
529
+ let config2 = await getCompleteConfig(component.configFiles, now);
530
+ const builtComponentPath = sourcePathToDistPath(component.path);
531
+ const componentImport = await import(builtComponentPath + `?cache=${Date.now()}`);
532
+ if (componentImport?.config) {
533
+ config2 = {
534
+ ...config2,
535
+ ...componentImport.config
536
+ };
537
+ }
538
+ if (config2?.staticPaths) buildStaticFiles(config2, component, componentImport, builtComponentPath);
539
+ }
540
+ }
541
+ async function buildStaticFiles(config2, component, componentImport, builtComponentPath) {
542
+ if (!config2?.staticPaths) return;
543
+ let componentFileName = component.path.split("/").pop().replace(regex_default.endsWithJSX, "").replace(regex_default.endsWithTSX, "");
544
+ const isDynamicRoute = component.path.match(regex_default.isDynamicRoute);
545
+ if (isDynamicRoute) {
546
+ componentFileName = component.path.split("/").filter((dir) => dir.match(regex_default.isDynamicRoute)).join("/").replace(regex_default.endsWithJSX, "").replace(regex_default.endsWithTSX, "");
547
+ }
548
+ const componentDynamicPaths = getComponentDynamicPaths(componentFileName);
549
+ const componentFn = componentImport.default;
550
+ const outputPath = componentDynamicPaths?.length ? componentDynamicPaths.reduce((total, current) => {
551
+ return total.replace(`/[${current}]`, "");
552
+ }, path5.dirname(builtComponentPath)) : path5.dirname(builtComponentPath);
553
+ if (typeof config2?.staticPaths === "boolean") {
554
+ try {
555
+ const req = { params: {} };
556
+ const data = config2?.data ? await config2.data(req) : null;
557
+ const staticComponentOutput = await componentFn({ data });
558
+ fs5.writeFileSync(
559
+ path5.join(outputPath, "./index.html"),
560
+ doctypeHTML + (config2?.wrapper ? await config2.wrapper({ req, children: staticComponentOutput, config: config2, data }) : staticComponentOutput) + staticComment
561
+ );
562
+ } catch (err) {
563
+ console.error(err);
564
+ console.log("Could not build static component", component.path);
565
+ }
566
+ } else if (typeof config2?.staticPaths === "function") {
567
+ const dynamicPaths = await config2.staticPaths();
568
+ for (const dynamicPath of dynamicPaths) {
569
+ try {
570
+ const req = {
571
+ params: {
572
+ ...componentDynamicPaths?.length ? dynamicPath : {}
539
573
  }
540
- }
574
+ };
575
+ const data = config2?.data ? await config2.data(req) : null;
576
+ const staticComponentOutput = await componentFn({ req, data });
577
+ const updatedOutDir = path5.join(outputPath, `./${componentDynamicPaths.map((key) => dynamicPath[key]).join("/")}`);
578
+ fs5.ensureDirSync(updatedOutDir);
579
+ fs5.writeFileSync(
580
+ path5.join(updatedOutDir, "./index.html"),
581
+ doctypeHTML + (config2?.wrapper ? await config2.wrapper({ req, children: staticComponentOutput, config: config2, data }) : staticComponentOutput) + staticComment
582
+ );
583
+ } catch (err) {
584
+ console.log("Could not build static component", component.path);
585
+ console.error(err);
541
586
  }
542
587
  }
543
588
  }
544
589
  }
545
- function sourcePathToDistPath(sourcePath) {
546
- return sourcePath.replace(config.srcDir, config.distDir).replace(/\.ts$/, ".js").replace(/\.tsx$/, ".js");
547
- }
548
590
  function getComponentDynamicPaths(componentPath) {
549
591
  const matches = [...componentPath.matchAll(regex_default.dynamicRoutes)];
550
592
  if (!matches?.length) return null;
@@ -593,6 +635,7 @@ async function loadSecretsManagerSecrets() {
593
635
  await setupEnv();
594
636
  async function runDevServer() {
595
637
  process.env.NODE_ENV = "development";
638
+ const rebuildEmitter = createRebuildEmitter();
596
639
  await buildApp(true);
597
640
  const startServer = (await import(config.serverDistAppPath + `?cache=${Date.now()}`)).default;
598
641
  let appServer = await startServer();
@@ -604,15 +647,21 @@ async function runDevServer() {
604
647
  watcher.on("all", async (event, path8) => {
605
648
  const shouldReloadServer = path8.startsWith(config.serverDir) && !path8.startsWith(config.runDir) || ["add", "unlink"].includes(event) && path8.startsWith(config.pagesDir);
606
649
  if (shouldReloadServer) {
607
- await appServer.server.close();
608
- await buildApp(true);
609
- const startServer2 = (await import(config.serverDistAppPath + `?cache=${Date.now()}`)).default;
610
- appServer = await startServer2();
650
+ await asyncServerClose(appServer.server);
651
+ rebuildEmitter.emit("rebuild-server");
611
652
  return;
612
653
  }
613
654
  await buildApp(true);
614
655
  refreshEmitter.emit("refresh");
615
656
  });
657
+ rebuildEmitter.on("done", async () => {
658
+ await rebuildServer();
659
+ });
660
+ async function rebuildServer() {
661
+ await buildApp(true);
662
+ const startServer2 = (await import(config.serverDistAppPath + `?cache=${Date.now()}`)).default;
663
+ appServer = await startServer2();
664
+ }
616
665
  const wsApp = express();
617
666
  const wsServer = http.createServer(wsApp);
618
667
  expressWs(wsApp, wsServer);
@@ -632,6 +681,37 @@ async function runDevServer() {
632
681
  console.info(`Dev server listening on port ${wsPort}`);
633
682
  });
634
683
  }
684
+ function asyncServerClose(server) {
685
+ return new Promise((resolve, reject) => {
686
+ server.close();
687
+ resolve(true);
688
+ });
689
+ }
690
+ function createRebuildEmitter(delay = 500) {
691
+ class RebuildEmitter extends EventEmitter {
692
+ }
693
+ ;
694
+ const rebuildEmitter = new RebuildEmitter();
695
+ let start = 0;
696
+ let hasEmitted = false;
697
+ rebuildEmitter.on("rebuild-server", () => {
698
+ hasEmitted = false;
699
+ trigger();
700
+ });
701
+ function trigger() {
702
+ start = Date.now();
703
+ const interval = setInterval(() => {
704
+ if (hasEmitted) return;
705
+ const now = Date.now();
706
+ if (now - start >= delay) {
707
+ rebuildEmitter.emit("done");
708
+ clearInterval(interval);
709
+ hasEmitted = true;
710
+ }
711
+ }, 100);
712
+ }
713
+ return rebuildEmitter;
714
+ }
635
715
 
636
716
  // src/scripts/xpine-dev.ts
637
717
  runDevServer();
@@ -6,7 +6,8 @@ async function replaceDocumentContentsWithLinkResponse() {
6
6
  }
7
7
  }
8
8
  async function updatePageOnLinkClick(e) {
9
- const targetHref = e?.target?.getAttribute("href");
9
+ e.preventDefault();
10
+ const targetHref = e?.target?.closest("a")?.getAttribute("href");
10
11
  if (!targetHref) return;
11
12
  if (isExternalURL(targetHref) && !e?.target?.getAttribute("data-spa-crossorigin")) return;
12
13
  e.preventDefault();
@@ -0,0 +1,6 @@
1
+ import { ConfigFile } from '../../types';
2
+ export declare function sourcePathToDistPath(sourcePath: string): string;
3
+ export declare function getConfigFiles(pathName: string, pageConfigFiles: string[], returnDistPaths?: boolean): string[] | null;
4
+ export declare function getCompleteConfig(configFiles: string[], cacheKey: string | number): Promise<ConfigFile>;
5
+ export declare function isAConfigFile(pathName: string): boolean;
6
+ //# sourceMappingURL=config-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-file.d.ts","sourceRoot":"","sources":["../../../src/util/config-file.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,UAEtD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,EAAE,eAAe,GAAE,OAAc,GAAG,MAAM,EAAE,GAAG,IAAI,CAY5H;AAED,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,uBASvF;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,WAE7C"}
@@ -0,0 +1,3 @@
1
+ export declare const doctypeHTML = "<!DOCTYPE html>";
2
+ export declare const staticComment = "<!-- static -->";
3
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/util/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,aAAa,oBAAoB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"get-config.d.ts","sourceRoot":"","sources":["../../../src/util/get-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI1C,wBAAgB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,UAGzC;AAoDD,eAAO,MAAM,MAAM,EAAE,WAGpB,CAAC"}
1
+ {"version":3,"file":"get-config.d.ts","sourceRoot":"","sources":["../../../src/util/get-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI1C,wBAAgB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,UAGzC;AAuDD,eAAO,MAAM,MAAM,EAAE,WAGpB,CAAC"}
@@ -1,4 +1,4 @@
1
- import { PluginCreator } from "postcss";
1
+ import { PluginCreator } from 'postcss';
2
2
  declare namespace plugin {
3
3
  interface Options {
4
4
  }
@@ -0,0 +1,11 @@
1
+ declare const _default: {
2
+ dotTsx: RegExp;
3
+ hasLetters: RegExp;
4
+ configFile: RegExp;
5
+ dynamicRoutes: RegExp;
6
+ isDynamicRoute: RegExp;
7
+ endsWithTSX: RegExp;
8
+ endsWithJSX: RegExp;
9
+ };
10
+ export default _default;
11
+ //# sourceMappingURL=regex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regex.d.ts","sourceRoot":"","sources":["../../../src/util/regex.ts"],"names":[],"mappings":";;;;;;;;;AAAA,wBAQE"}