xpine 0.0.20 → 0.0.22

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 +357 -128
  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 +249 -104
  15. package/dist/src/scripts/xpine-dev.js +294 -114
  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 -5
  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
@@ -8,11 +8,10 @@ import EventEmitter from "events";
8
8
  import chokidar from "chokidar";
9
9
 
10
10
  // src/scripts/build.ts
11
- import path3 from "path";
12
- import fs2 from "fs-extra";
11
+ import path5 from "path";
12
+ import fs5 from "fs-extra";
13
13
  import { build } from "esbuild";
14
- import builtinModules from "builtin-modules";
15
- import ts2 from "typescript";
14
+ import ts3 from "typescript";
16
15
 
17
16
  // src/build/typescript-builder.ts
18
17
  import ts from "typescript";
@@ -116,17 +115,6 @@ function removeClientScriptInTSXFile(pathName, source) {
116
115
  clientDataStart
117
116
  };
118
117
  }
119
- function createStaticFile(pathName, source) {
120
- source.forEachChild((child) => {
121
- if (child.kind === ts.SyntaxKind.ExpressionStatement) {
122
- const text = child.getText(source);
123
- const cleanedText = text.replace(/["';]/g, "");
124
- if (cleanedText === "xpine-static") {
125
- console.log("make this file static", pathName);
126
- }
127
- }
128
- });
129
- }
130
118
 
131
119
  // src/scripts/build.ts
132
120
  import { globSync } from "glob";
@@ -142,6 +130,10 @@ var require_default = createRequire(import.meta.url);
142
130
  function getXPineDistDir() {
143
131
  const dir = import.meta.dirname;
144
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
+ }
145
137
  return splitDir[0] + "/xpine/dist";
146
138
  }
147
139
 
@@ -183,6 +175,9 @@ var configDefaults = {
183
175
  get pagesDir() {
184
176
  return path2.join(this.srcDir, "./pages");
185
177
  },
178
+ get distPagesDir() {
179
+ return path2.join(this.distDir, "./pages");
180
+ },
186
181
  get publicDir() {
187
182
  return path2.join(this.srcDir, "./public");
188
183
  },
@@ -218,21 +213,152 @@ var plugin = (opts = {}) => {
218
213
  plugin.postcss = true;
219
214
  var remove_layers_default = plugin;
220
215
 
216
+ // src/build/esbuild/transformTSXFiles.ts
217
+ import fs2 from "fs-extra";
218
+ import ts2 from "typescript";
219
+
220
+ // src/util/regex.ts
221
+ var regex_default = {
222
+ dotTsx: /.tsx/,
223
+ hasLetters: /[A-Za-z0-9]/g,
224
+ configFile: /\+config\.[tj]sx?/g,
225
+ dynamicRoutes: /(\[)(.*?)(\])/g,
226
+ isDynamicRoute: /\[(.*)\]/g,
227
+ endsWithTSX: /\.tsx$/,
228
+ endsWithJSX: /\.jsx$/
229
+ };
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
+
262
+ // src/build/esbuild/transformTSXFiles.ts
263
+ function transformTSXFiles(componentData, pageConfigFiles) {
264
+ return {
265
+ name: "transform-tsx-files",
266
+ setup(build2) {
267
+ build2.onLoad({ filter: regex_default.dotTsx }, async (args) => {
268
+ const content = fs2.readFileSync(args.path, "utf-8");
269
+ const source = ts2.createSourceFile(
270
+ args.path,
271
+ content,
272
+ ts2.ScriptTarget.Latest
273
+ );
274
+ const cleanedContent = removeClientScriptInTSXFile(args.path, source);
275
+ const htmlImportStart = "import { html } from 'xpine';\n";
276
+ const newContent = `${htmlImportStart}${cleanedContent.content}`;
277
+ componentData.push({
278
+ ...args,
279
+ contents: `${htmlImportStart}${cleanedContent.fullContent}`,
280
+ clientContent: cleanedContent.clientContent,
281
+ configFiles: isAConfigFile(args?.path) ? null : getConfigFiles(args.path, pageConfigFiles)
282
+ });
283
+ return {
284
+ contents: newContent,
285
+ loader: "tsx"
286
+ };
287
+ });
288
+ }
289
+ };
290
+ }
291
+
292
+ // src/build/esbuild/addDotJS.ts
293
+ import fs3 from "fs-extra";
294
+ import path4 from "path";
295
+ import builtinModules from "builtin-modules";
296
+ function addDotJS(allPackages2, extensions2, isDev) {
297
+ const allPackagesIncludingNode = allPackages2.concat(builtinModules);
298
+ return {
299
+ name: "add-dot-js",
300
+ setup(build2) {
301
+ build2.onResolve({ filter: /.*/ }, (args) => {
302
+ const hasAtSign = args.path.startsWith("@");
303
+ const isPackage = hasAtSign ? allPackagesIncludingNode.includes(args.path) : allPackagesIncludingNode.includes(args.path.split("/").shift());
304
+ if (args.importer && !isPackage) {
305
+ const calculatedDir = path4.join(args.resolveDir, args.path);
306
+ let existsAsFile = false;
307
+ for (const extension of extensions2) {
308
+ const asFile = calculatedDir + extension;
309
+ const exists = fs3.existsSync(asFile);
310
+ if (exists) existsAsFile = true;
311
+ }
312
+ let outputPath = args.path + (existsAsFile ? "" : "/index") + ".js";
313
+ outputPath = args.path.endsWith(".js") || args.path.endsWith(".mjs") ? args.path : outputPath;
314
+ return { path: outputPath + (isDev ? `?cache=${Date.now()}` : ""), external: true };
315
+ }
316
+ });
317
+ }
318
+ };
319
+ }
320
+
321
+ // src/build/esbuild/getDataFiles.ts
322
+ import fs4 from "fs-extra";
323
+ function getDataFiles(dataFiles) {
324
+ return {
325
+ name: "get-data-files",
326
+ setup(build2) {
327
+ build2.onLoad({ filter: /\.data\.(js|mjs|ts)$/ }, (args) => {
328
+ const contents = fs4.readFileSync(args.path, "utf-8");
329
+ dataFiles.push({
330
+ ...args,
331
+ contents
332
+ });
333
+ return {
334
+ contents,
335
+ loader: "ts"
336
+ };
337
+ });
338
+ }
339
+ };
340
+ }
341
+
342
+ // src/util/constants.ts
343
+ var doctypeHTML = "<!DOCTYPE html>";
344
+ var staticComment = "<!-- static -->";
345
+
221
346
  // src/scripts/build.ts
222
347
  var extensions = [".ts", ".tsx"];
223
- var packageJson = JSON.parse(fs2.readFileSync(config.packageJsonPath, "utf-8"));
348
+ var packageJson = JSON.parse(fs5.readFileSync(config.packageJsonPath, "utf-8"));
224
349
  var allPackages = Object.keys(packageJson.devDependencies).concat(Object.keys(packageJson.dependencies));
225
- var allPackagesIncludingNode = allPackages.concat(builtinModules);
226
350
  var xpineDistDir = getXPineDistDir();
227
- async function buildApp(isDev = false) {
351
+ async function buildApp(isDev = false, removePreviousBuild = false) {
228
352
  try {
353
+ if (removePreviousBuild) fs5.removeSync(config.distDir);
229
354
  const srcDirFiles = globSync(config.srcDir + "/**/*.{js,ts,tsx,jsx}");
230
355
  const { componentData, dataFiles } = await buildAppFiles(srcDirFiles, isDev);
231
356
  const alpineDataFile = await buildAlpineDataFile(componentData, dataFiles);
232
357
  await buildClientSideFiles([alpineDataFile], isDev);
233
- fs2.removeSync(config.distTempFolder);
358
+ fs5.removeSync(config.distTempFolder);
234
359
  await buildCSS();
235
360
  await buildPublicFolderSymlinks();
361
+ if (!isDev) await buildFilesWithConfigs(componentData);
236
362
  } catch (err) {
237
363
  console.error("Build failed");
238
364
  console.error(err);
@@ -241,8 +367,12 @@ async function buildApp(isDev = false) {
241
367
  async function buildAppFiles(files, isDev) {
242
368
  const componentData = [];
243
369
  const dataFiles = [];
370
+ const pageConfigFiles = files.filter((file) => {
371
+ const fileName = file.split("/").at(-1).split(".").shift();
372
+ return fileName === "+config";
373
+ });
244
374
  const backendFiles = files.filter((file) => extensions.find((ext) => file.endsWith(ext))).filter((file) => !file.startsWith(config.publicDir));
245
- fs2.ensureDirSync(config.distDir);
375
+ fs5.ensureDirSync(config.distDir);
246
376
  await build({
247
377
  entryPoints: backendFiles,
248
378
  format: "esm",
@@ -254,69 +384,9 @@ async function buildAppFiles(files, isDev) {
254
384
  jsx: "transform",
255
385
  minify: !isDev,
256
386
  plugins: [
257
- {
258
- name: "add-dot-js",
259
- setup(build2) {
260
- build2.onResolve({ filter: /.*/ }, (args) => {
261
- const hasAtSign = args.path.startsWith("@");
262
- const isPackage = hasAtSign ? allPackagesIncludingNode.includes(args.path) : allPackagesIncludingNode.includes(args.path.split("/").shift());
263
- if (args.importer && !isPackage) {
264
- const calculatedDir = path3.join(args.resolveDir, args.path);
265
- let existsAsFile = false;
266
- for (const extension of extensions) {
267
- const asFile = calculatedDir + extension;
268
- const exists = fs2.existsSync(asFile);
269
- if (exists) existsAsFile = true;
270
- }
271
- let outputPath = args.path + (existsAsFile ? "" : "/index") + ".js";
272
- outputPath = args.path.endsWith(".js") || args.path.endsWith(".mjs") ? args.path : outputPath;
273
- return { path: outputPath + (isDev ? `?cache=${Date.now()}` : ""), external: true };
274
- }
275
- });
276
- }
277
- },
278
- {
279
- name: "insert-html-banner-and-remove-client-scripts",
280
- setup(build2) {
281
- build2.onLoad({ filter: /.tsx/ }, (args) => {
282
- const content = fs2.readFileSync(args.path, "utf-8");
283
- const source = ts2.createSourceFile(
284
- args.path,
285
- content,
286
- ts2.ScriptTarget.Latest
287
- );
288
- const cleanedContent = removeClientScriptInTSXFile(args.path, source);
289
- createStaticFile(args.path, source);
290
- const htmlImportStart = "import { html } from 'xpine';\n";
291
- const newContent = `${htmlImportStart}${cleanedContent.content}`;
292
- componentData.push({
293
- ...args,
294
- contents: `${htmlImportStart}${cleanedContent.fullContent}`,
295
- clientContent: cleanedContent.clientContent
296
- });
297
- return {
298
- contents: newContent,
299
- loader: "tsx"
300
- };
301
- });
302
- }
303
- },
304
- {
305
- name: "get-data-files",
306
- setup(build2) {
307
- build2.onLoad({ filter: /\.data\.(js|mjs|ts)$/ }, (args) => {
308
- const contents = fs2.readFileSync(args.path, "utf-8");
309
- dataFiles.push({
310
- ...args,
311
- contents
312
- });
313
- return {
314
- contents,
315
- loader: "ts"
316
- };
317
- });
318
- }
319
- }
387
+ addDotJS(allPackages, extensions, isDev),
388
+ transformTSXFiles(componentData, pageConfigFiles),
389
+ getDataFiles(dataFiles)
320
390
  ]
321
391
  });
322
392
  await logSize(config.distDir, "app");
@@ -326,8 +396,8 @@ async function buildAppFiles(files, isDev) {
326
396
  };
327
397
  }
328
398
  async function buildClientSideFiles(alpineDataFiles = [], isDev) {
329
- const tempFilePath = path3.join(config.distTempFolder, "./app.ts");
330
- fs2.ensureFileSync(tempFilePath);
399
+ const tempFilePath = path5.join(config.distTempFolder, "./app.ts");
400
+ fs5.ensureFileSync(tempFilePath);
331
401
  const pagesScriptsGlob = config.publicDir + "/scripts/pages/**/*.{js,ts}";
332
402
  const clientFiles = globSync(
333
403
  config.publicDir + "/**/*.{js,ts}",
@@ -356,16 +426,14 @@ async function buildClientSideFiles(alpineDataFiles = [], isDev) {
356
426
  await logSize(config.distPublicDir, "client");
357
427
  }
358
428
  function writeDevServerClientSideCode(tempFilePath) {
359
- const devServerPath = path3.join(xpineDistDir, "./src/static/dev-server.js");
360
- const content = fs2.readFileSync(devServerPath, "utf-8");
361
- fs2.appendFileSync(tempFilePath, `
362
- ` + content);
429
+ const devServerPath = path5.join(xpineDistDir, "./src/static/dev-server.js");
430
+ const content = fs5.readFileSync(devServerPath, "utf-8");
431
+ fs5.appendFileSync(tempFilePath, "\n" + content);
363
432
  }
364
433
  function writeSpaClientSideCode(tempFilePath) {
365
- const spaPath = path3.join(xpineDistDir, "./src/static/spa.js");
366
- const content = fs2.readFileSync(spaPath, "utf-8");
367
- fs2.appendFileSync(tempFilePath, `
368
- ` + content);
434
+ const spaPath = path5.join(xpineDistDir, "./src/static/spa.js");
435
+ const content = fs5.readFileSync(spaPath, "utf-8");
436
+ fs5.appendFileSync(tempFilePath, "\n" + content);
369
437
  }
370
438
  async function buildAlpineDataFile(componentData, dataFiles) {
371
439
  const output = {
@@ -384,10 +452,10 @@ async function buildAlpineDataFile(componentData, dataFiles) {
384
452
  };
385
453
  const componentsAndDataFiles = componentData.concat(dataFiles);
386
454
  for (const component of componentsAndDataFiles) {
387
- const sourceFile = ts2.createSourceFile(
455
+ const sourceFile = ts3.createSourceFile(
388
456
  component.path,
389
457
  component.contents,
390
- ts2.ScriptTarget.Latest
458
+ ts3.ScriptTarget.Latest
391
459
  );
392
460
  const dataFunctionResult = findDataAttributesAndFunctions(sourceFile, sourceFile);
393
461
  dataFunctionResults.foundDataAttributes.push(...dataFunctionResult.foundDataAttributes);
@@ -409,18 +477,18 @@ async function buildAlpineDataFile(componentData, dataFiles) {
409
477
  output.code.push(`Alpine.data('${dataFunction.name}', ${dataFunction.name});`);
410
478
  }
411
479
  const result = output.imports.join("\n") + "\n" + output.code.join("\n") + "\n" + output.end.join("\n");
412
- fs2.ensureFileSync(config.alpineDataPath);
413
- fs2.writeFileSync(config.alpineDataPath, result);
480
+ fs5.ensureFileSync(config.alpineDataPath);
481
+ fs5.writeFileSync(config.alpineDataPath, result);
414
482
  return config.alpineDataPath;
415
483
  }
416
484
  async function buildCSS() {
417
485
  const cssFiles = globSync(config.srcDir + "/**/*.css");
418
486
  for (const file of cssFiles) {
419
- const fileContents = fs2.readFileSync(file, "utf-8");
487
+ const fileContents = fs5.readFileSync(file, "utf-8");
420
488
  const result = await postcss([tailwindPostcss(), remove_layers_default()]).process(fileContents, { from: file });
421
489
  const newPath = file.replace(config.srcDir, config.distDir);
422
- fs2.ensureFileSync(newPath);
423
- fs2.writeFileSync(newPath, result.css);
490
+ fs5.ensureFileSync(newPath);
491
+ fs5.writeFileSync(newPath, result.css);
424
492
  }
425
493
  logSize(config.distPublicDir, "css");
426
494
  }
@@ -434,8 +502,8 @@ async function buildPublicFolderSymlinks() {
434
502
  const splitNewPath = newPath.split("/");
435
503
  splitNewPath.pop();
436
504
  const newDir = splitNewPath.join("/");
437
- fs2.ensureDirSync(newDir);
438
- fs2.symlinkSync(file, newPath);
505
+ fs5.ensureDirSync(newDir);
506
+ fs5.symlinkSync(file, newPath);
439
507
  } catch {
440
508
  }
441
509
  }
@@ -446,7 +514,7 @@ async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
446
514
  if (!validExtensions.find((ext) => file.endsWith(ext))) return false;
447
515
  return {
448
516
  file,
449
- size: fs2.statSync(file).size / (1024 * 1e3)
517
+ size: fs5.statSync(file).size / (1024 * 1e3)
450
518
  };
451
519
  }).filter(Boolean);
452
520
  const totalSize = fileSizes.reduce((total, current) => {
@@ -454,17 +522,91 @@ async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
454
522
  }, 0);
455
523
  console.info(`[${totalSize.toFixed(3)} MB] Built ${type}`);
456
524
  }
525
+ async function buildFilesWithConfigs(componentData) {
526
+ const now = Date.now();
527
+ const componentsWithConfigs = componentData.filter((item) => item.configFiles);
528
+ for (const component of componentsWithConfigs) {
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 : {}
573
+ }
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);
586
+ }
587
+ }
588
+ }
589
+ }
590
+ function getComponentDynamicPaths(componentPath) {
591
+ const matches = [...componentPath.matchAll(regex_default.dynamicRoutes)];
592
+ if (!matches?.length) return null;
593
+ const output = [];
594
+ for (const match of matches) {
595
+ output.push(match[2]);
596
+ }
597
+ return output;
598
+ }
457
599
 
458
600
  // src/runDevServer.ts
459
- import path5 from "path";
601
+ import path7 from "path";
460
602
 
461
603
  // src/util/env.ts
462
604
  import dotenv from "dotenv";
463
- import path4 from "path";
605
+ import path6 from "path";
464
606
  import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
465
607
  async function setupEnv() {
466
608
  if (process.env.HAS_SETUP_ENV) return;
467
- dotenv.config({ path: path4.join(config.rootDir, `./.env.${process.env.STAGE || "dev"}`) });
609
+ dotenv.config({ path: path6.join(config.rootDir, `./.env.${process.env.STAGE || "dev"}`) });
468
610
  await loadSecretsManagerSecrets();
469
611
  process.env.HAS_SETUP_ENV = "true";
470
612
  }
@@ -493,26 +635,33 @@ async function loadSecretsManagerSecrets() {
493
635
  await setupEnv();
494
636
  async function runDevServer() {
495
637
  process.env.NODE_ENV = "development";
496
- const startServer = (await import(config.serverDistAppPath + `?cache=${Date.now()}`)).default;
638
+ const rebuildEmitter = createRebuildEmitter();
497
639
  await buildApp(true);
640
+ const startServer = (await import(config.serverDistAppPath + `?cache=${Date.now()}`)).default;
498
641
  let appServer = await startServer();
499
642
  const watcher = chokidar.watch(config.srcDir, {
500
643
  ignoreInitial: true,
501
644
  // Ignore map and prisma files
502
- ignored: (pathName) => pathName.endsWith(".map") || pathName.startsWith(path5.join(config.serverDir, "./prisma"))
645
+ ignored: (pathName) => pathName.endsWith(".map") || pathName.startsWith(path7.join(config.serverDir, "./prisma"))
503
646
  });
504
- watcher.on("all", async (event, path6) => {
505
- const shouldReloadServer = path6.startsWith(config.serverDir) && !path6.startsWith(config.runDir) || ["add", "unlink"].includes(event) && path6.startsWith(config.pagesDir);
647
+ watcher.on("all", async (event, path8) => {
648
+ const shouldReloadServer = path8.startsWith(config.serverDir) && !path8.startsWith(config.runDir) || ["add", "unlink"].includes(event) && path8.startsWith(config.pagesDir);
506
649
  if (shouldReloadServer) {
507
- await appServer.server.close();
508
- await buildApp(true);
509
- const startServer2 = (await import(config.serverDistAppPath + `?cache=${Date.now()}`)).default;
510
- appServer = await startServer2();
650
+ await asyncServerClose(appServer.server);
651
+ rebuildEmitter.emit("rebuild-server");
511
652
  return;
512
653
  }
513
654
  await buildApp(true);
514
655
  refreshEmitter.emit("refresh");
515
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
+ }
516
665
  const wsApp = express();
517
666
  const wsServer = http.createServer(wsApp);
518
667
  expressWs(wsApp, wsServer);
@@ -532,6 +681,37 @@ async function runDevServer() {
532
681
  console.info(`Dev server listening on port ${wsPort}`);
533
682
  });
534
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
+ }
535
715
 
536
716
  // src/scripts/xpine-dev.ts
537
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"}
@@ -1 +1 @@
1
- {"version":3,"file":"require.d.ts","sourceRoot":"","sources":["../../../src/util/require.ts"],"names":[],"mappings":";AAEA,wBAA8C;AAE9C,wBAAgB,eAAe,WAI9B"}
1
+ {"version":3,"file":"require.d.ts","sourceRoot":"","sources":["../../../src/util/require.ts"],"names":[],"mappings":";AAEA,wBAA8C;AAE9C,wBAAgB,eAAe,WAS9B"}
package/dist/types.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { Request } from 'express';
1
+ import { Request, Response } from 'express';
2
2
  export type XPineConfig = {
3
- [key: string]: string;
3
+ [key: string]: any;
4
4
  };
5
5
  export type TokenUser = {
6
6
  email?: string;
@@ -8,5 +8,34 @@ export type TokenUser = {
8
8
  };
9
9
  export type ServerRequest = Request & {
10
10
  user?: TokenUser;
11
+ clientIp?: string;
12
+ };
13
+ export type WrapperProps = {
14
+ req: ServerRequest;
15
+ children: any;
16
+ config: ConfigFile;
17
+ data?: any;
18
+ };
19
+ export type ConfigFile = {
20
+ staticPaths?: boolean | (() => Promise<{
21
+ [key: string]: string;
22
+ }[]>);
23
+ wrapper?: (props: WrapperProps) => Promise<any>;
24
+ data?: (req: ServerRequest) => Promise<any>;
25
+ };
26
+ export type PageProps = {
27
+ req: ServerRequest;
28
+ res: Response;
29
+ data: any;
30
+ };
31
+ export type FileItem = {
32
+ file: string;
33
+ size: number;
34
+ };
35
+ export type ComponentData = {
36
+ path: string;
37
+ contents: string;
38
+ clientContent: string;
39
+ configFiles: string[];
11
40
  };
12
41
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG;IACpC,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5C,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG;IACpC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,aAAa,CAAC;IACnB,QAAQ,EAAE,GAAG,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,WAAW,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,EAAE,CAAC,CAAC,CAAC;IACpE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CAC7C,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,aAAa,CAAC;IACnB,GAAG,EAAE,QAAQ,CAAC;IACd,IAAI,EAAE,GAAG,CAAC;CACX,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAA"}