xpine 0.0.19 → 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.
@@ -1,11 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/scripts/build.ts
4
- import path3 from "path";
5
- import fs2 from "fs-extra";
4
+ import path5 from "path";
5
+ import fs5 from "fs-extra";
6
6
  import { build } from "esbuild";
7
- import builtinModules from "builtin-modules";
8
- import ts2 from "typescript";
7
+ import ts3 from "typescript";
9
8
 
10
9
  // src/build/typescript-builder.ts
11
10
  import ts from "typescript";
@@ -109,17 +108,6 @@ function removeClientScriptInTSXFile(pathName, source) {
109
108
  clientDataStart
110
109
  };
111
110
  }
112
- function createStaticFile(pathName, source) {
113
- source.forEachChild((child) => {
114
- if (child.kind === ts.SyntaxKind.ExpressionStatement) {
115
- const text = child.getText(source);
116
- const cleanedText = text.replace(/["';]/g, "");
117
- if (cleanedText === "xpine-static") {
118
- console.log("make this file static", pathName);
119
- }
120
- }
121
- });
122
- }
123
111
 
124
112
  // src/scripts/build.ts
125
113
  import { globSync } from "glob";
@@ -176,6 +164,9 @@ var configDefaults = {
176
164
  get pagesDir() {
177
165
  return path2.join(this.srcDir, "./pages");
178
166
  },
167
+ get distPagesDir() {
168
+ return path2.join(this.distDir, "./pages");
169
+ },
179
170
  get publicDir() {
180
171
  return path2.join(this.srcDir, "./public");
181
172
  },
@@ -211,11 +202,117 @@ var plugin = (opts = {}) => {
211
202
  plugin.postcss = true;
212
203
  var remove_layers_default = plugin;
213
204
 
205
+ // src/build/esbuild/transformTSXFiles.ts
206
+ import fs2 from "fs-extra";
207
+ import ts2 from "typescript";
208
+ import path3 from "path";
209
+
210
+ // src/util/regex.ts
211
+ var regex_default = {
212
+ dotTsx: /.tsx/,
213
+ hasLetters: /[A-Za-z0-9]/g,
214
+ configFile: /\+config\.[tj]s/g,
215
+ dynamicRoutes: /(\[)(.*?)(\])/g,
216
+ isDynamicRoute: /\[(.*)\]/g,
217
+ endsWithTSX: /\.tsx$/,
218
+ endsWithJSX: /\.jsx$/
219
+ };
220
+
221
+ // src/build/esbuild/transformTSXFiles.ts
222
+ function transformTSXFiles(componentData, pageConfigFiles) {
223
+ return {
224
+ name: "transform-tsx-files",
225
+ setup(build2) {
226
+ build2.onLoad({ filter: regex_default.dotTsx }, (args) => {
227
+ const configFile = getConfigFile(args.path, pageConfigFiles);
228
+ const content = fs2.readFileSync(args.path, "utf-8");
229
+ const source = ts2.createSourceFile(
230
+ args.path,
231
+ content,
232
+ ts2.ScriptTarget.Latest
233
+ );
234
+ const cleanedContent = removeClientScriptInTSXFile(args.path, source);
235
+ const htmlImportStart = "import { html } from 'xpine';\n";
236
+ const newContent = `${htmlImportStart}${cleanedContent.content}`;
237
+ componentData.push({
238
+ ...args,
239
+ contents: `${htmlImportStart}${cleanedContent.fullContent}`,
240
+ clientContent: cleanedContent.clientContent,
241
+ configFile
242
+ });
243
+ return {
244
+ contents: newContent,
245
+ loader: "tsx"
246
+ };
247
+ });
248
+ }
249
+ };
250
+ }
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
+
262
+ // src/build/esbuild/addDotJS.ts
263
+ import fs3 from "fs-extra";
264
+ import path4 from "path";
265
+ import builtinModules from "builtin-modules";
266
+ function addDotJS(allPackages2, extensions2, isDev) {
267
+ const allPackagesIncludingNode = allPackages2.concat(builtinModules);
268
+ return {
269
+ name: "add-dot-js",
270
+ setup(build2) {
271
+ build2.onResolve({ filter: /.*/ }, (args) => {
272
+ const hasAtSign = args.path.startsWith("@");
273
+ const isPackage = hasAtSign ? allPackagesIncludingNode.includes(args.path) : allPackagesIncludingNode.includes(args.path.split("/").shift());
274
+ if (args.importer && !isPackage) {
275
+ const calculatedDir = path4.join(args.resolveDir, args.path);
276
+ let existsAsFile = false;
277
+ for (const extension of extensions2) {
278
+ const asFile = calculatedDir + extension;
279
+ const exists = fs3.existsSync(asFile);
280
+ if (exists) existsAsFile = true;
281
+ }
282
+ let outputPath = args.path + (existsAsFile ? "" : "/index") + ".js";
283
+ outputPath = args.path.endsWith(".js") || args.path.endsWith(".mjs") ? args.path : outputPath;
284
+ return { path: outputPath + (isDev ? `?cache=${Date.now()}` : ""), external: true };
285
+ }
286
+ });
287
+ }
288
+ };
289
+ }
290
+
291
+ // src/build/esbuild/getDataFiles.ts
292
+ import fs4 from "fs-extra";
293
+ function getDataFiles(dataFiles) {
294
+ return {
295
+ name: "get-data-files",
296
+ setup(build2) {
297
+ build2.onLoad({ filter: /\.data\.(js|mjs|ts)$/ }, (args) => {
298
+ const contents = fs4.readFileSync(args.path, "utf-8");
299
+ dataFiles.push({
300
+ ...args,
301
+ contents
302
+ });
303
+ return {
304
+ contents,
305
+ loader: "ts"
306
+ };
307
+ });
308
+ }
309
+ };
310
+ }
311
+
214
312
  // src/scripts/build.ts
215
313
  var extensions = [".ts", ".tsx"];
216
- var packageJson = JSON.parse(fs2.readFileSync(config.packageJsonPath, "utf-8"));
314
+ var packageJson = JSON.parse(fs5.readFileSync(config.packageJsonPath, "utf-8"));
217
315
  var allPackages = Object.keys(packageJson.devDependencies).concat(Object.keys(packageJson.dependencies));
218
- var allPackagesIncludingNode = allPackages.concat(builtinModules);
219
316
  var xpineDistDir = getXPineDistDir();
220
317
  async function buildApp(isDev = false) {
221
318
  try {
@@ -223,9 +320,10 @@ async function buildApp(isDev = false) {
223
320
  const { componentData, dataFiles } = await buildAppFiles(srcDirFiles, isDev);
224
321
  const alpineDataFile = await buildAlpineDataFile(componentData, dataFiles);
225
322
  await buildClientSideFiles([alpineDataFile], isDev);
226
- fs2.removeSync(config.distTempFolder);
323
+ fs5.removeSync(config.distTempFolder);
227
324
  await buildCSS();
228
325
  await buildPublicFolderSymlinks();
326
+ if (!isDev) await buildStaticFiles(componentData);
229
327
  } catch (err) {
230
328
  console.error("Build failed");
231
329
  console.error(err);
@@ -234,8 +332,12 @@ async function buildApp(isDev = false) {
234
332
  async function buildAppFiles(files, isDev) {
235
333
  const componentData = [];
236
334
  const dataFiles = [];
335
+ const pageConfigFiles = files.filter((file) => {
336
+ const fileName = file.split("/").at(-1).split(".").shift();
337
+ return fileName === "+config";
338
+ });
237
339
  const backendFiles = files.filter((file) => extensions.find((ext) => file.endsWith(ext))).filter((file) => !file.startsWith(config.publicDir));
238
- fs2.ensureDirSync(config.distDir);
340
+ fs5.ensureDirSync(config.distDir);
239
341
  await build({
240
342
  entryPoints: backendFiles,
241
343
  format: "esm",
@@ -247,69 +349,9 @@ async function buildAppFiles(files, isDev) {
247
349
  jsx: "transform",
248
350
  minify: !isDev,
249
351
  plugins: [
250
- {
251
- name: "add-dot-js",
252
- setup(build2) {
253
- build2.onResolve({ filter: /.*/ }, (args) => {
254
- const hasAtSign = args.path.startsWith("@");
255
- const isPackage = hasAtSign ? allPackagesIncludingNode.includes(args.path) : allPackagesIncludingNode.includes(args.path.split("/").shift());
256
- if (args.importer && !isPackage) {
257
- const calculatedDir = path3.join(args.resolveDir, args.path);
258
- let existsAsFile = false;
259
- for (const extension of extensions) {
260
- const asFile = calculatedDir + extension;
261
- const exists = fs2.existsSync(asFile);
262
- if (exists) existsAsFile = true;
263
- }
264
- let outputPath = args.path + (existsAsFile ? "" : "/index") + ".js";
265
- outputPath = args.path.endsWith(".js") || args.path.endsWith(".mjs") ? args.path : outputPath;
266
- return { path: outputPath + (isDev ? `?cache=${Date.now()}` : ""), external: true };
267
- }
268
- });
269
- }
270
- },
271
- {
272
- name: "insert-html-banner-and-remove-client-scripts",
273
- setup(build2) {
274
- build2.onLoad({ filter: /.tsx/ }, (args) => {
275
- const content = fs2.readFileSync(args.path, "utf-8");
276
- const source = ts2.createSourceFile(
277
- args.path,
278
- content,
279
- ts2.ScriptTarget.Latest
280
- );
281
- const cleanedContent = removeClientScriptInTSXFile(args.path, source);
282
- createStaticFile(args.path, source);
283
- const htmlImportStart = "import { html } from 'xpine';\n";
284
- const newContent = `${htmlImportStart}${cleanedContent.content}`;
285
- componentData.push({
286
- ...args,
287
- contents: `${htmlImportStart}${cleanedContent.fullContent}`,
288
- clientContent: cleanedContent.clientContent
289
- });
290
- return {
291
- contents: newContent,
292
- loader: "tsx"
293
- };
294
- });
295
- }
296
- },
297
- {
298
- name: "get-data-files",
299
- setup(build2) {
300
- build2.onLoad({ filter: /\.data\.(js|mjs|ts)$/ }, (args) => {
301
- const contents = fs2.readFileSync(args.path, "utf-8");
302
- dataFiles.push({
303
- ...args,
304
- contents
305
- });
306
- return {
307
- contents,
308
- loader: "ts"
309
- };
310
- });
311
- }
312
- }
352
+ addDotJS(allPackages, extensions, isDev),
353
+ transformTSXFiles(componentData, pageConfigFiles),
354
+ getDataFiles(dataFiles)
313
355
  ]
314
356
  });
315
357
  await logSize(config.distDir, "app");
@@ -319,8 +361,8 @@ async function buildAppFiles(files, isDev) {
319
361
  };
320
362
  }
321
363
  async function buildClientSideFiles(alpineDataFiles = [], isDev) {
322
- const tempFilePath = path3.join(config.distTempFolder, "./app.ts");
323
- fs2.ensureFileSync(tempFilePath);
364
+ const tempFilePath = path5.join(config.distTempFolder, "./app.ts");
365
+ fs5.ensureFileSync(tempFilePath);
324
366
  const pagesScriptsGlob = config.publicDir + "/scripts/pages/**/*.{js,ts}";
325
367
  const clientFiles = globSync(
326
368
  config.publicDir + "/**/*.{js,ts}",
@@ -349,15 +391,15 @@ async function buildClientSideFiles(alpineDataFiles = [], isDev) {
349
391
  await logSize(config.distPublicDir, "client");
350
392
  }
351
393
  function writeDevServerClientSideCode(tempFilePath) {
352
- const devServerPath = path3.join(xpineDistDir, "./src/static/dev-server.js");
353
- const content = fs2.readFileSync(devServerPath, "utf-8");
354
- fs2.appendFileSync(tempFilePath, `
394
+ const devServerPath = path5.join(xpineDistDir, "./src/static/dev-server.js");
395
+ const content = fs5.readFileSync(devServerPath, "utf-8");
396
+ fs5.appendFileSync(tempFilePath, `
355
397
  ` + content);
356
398
  }
357
399
  function writeSpaClientSideCode(tempFilePath) {
358
- const spaPath = path3.join(xpineDistDir, "./src/static/spa.js");
359
- const content = fs2.readFileSync(spaPath, "utf-8");
360
- fs2.appendFileSync(tempFilePath, `
400
+ const spaPath = path5.join(xpineDistDir, "./src/static/spa.js");
401
+ const content = fs5.readFileSync(spaPath, "utf-8");
402
+ fs5.appendFileSync(tempFilePath, `
361
403
  ` + content);
362
404
  }
363
405
  async function buildAlpineDataFile(componentData, dataFiles) {
@@ -377,10 +419,10 @@ async function buildAlpineDataFile(componentData, dataFiles) {
377
419
  };
378
420
  const componentsAndDataFiles = componentData.concat(dataFiles);
379
421
  for (const component of componentsAndDataFiles) {
380
- const sourceFile = ts2.createSourceFile(
422
+ const sourceFile = ts3.createSourceFile(
381
423
  component.path,
382
424
  component.contents,
383
- ts2.ScriptTarget.Latest
425
+ ts3.ScriptTarget.Latest
384
426
  );
385
427
  const dataFunctionResult = findDataAttributesAndFunctions(sourceFile, sourceFile);
386
428
  dataFunctionResults.foundDataAttributes.push(...dataFunctionResult.foundDataAttributes);
@@ -402,18 +444,18 @@ async function buildAlpineDataFile(componentData, dataFiles) {
402
444
  output.code.push(`Alpine.data('${dataFunction.name}', ${dataFunction.name});`);
403
445
  }
404
446
  const result = output.imports.join("\n") + "\n" + output.code.join("\n") + "\n" + output.end.join("\n");
405
- fs2.ensureFileSync(config.alpineDataPath);
406
- fs2.writeFileSync(config.alpineDataPath, result);
447
+ fs5.ensureFileSync(config.alpineDataPath);
448
+ fs5.writeFileSync(config.alpineDataPath, result);
407
449
  return config.alpineDataPath;
408
450
  }
409
451
  async function buildCSS() {
410
452
  const cssFiles = globSync(config.srcDir + "/**/*.css");
411
453
  for (const file of cssFiles) {
412
- const fileContents = fs2.readFileSync(file, "utf-8");
454
+ const fileContents = fs5.readFileSync(file, "utf-8");
413
455
  const result = await postcss([tailwindPostcss(), remove_layers_default()]).process(fileContents, { from: file });
414
456
  const newPath = file.replace(config.srcDir, config.distDir);
415
- fs2.ensureFileSync(newPath);
416
- fs2.writeFileSync(newPath, result.css);
457
+ fs5.ensureFileSync(newPath);
458
+ fs5.writeFileSync(newPath, result.css);
417
459
  }
418
460
  logSize(config.distPublicDir, "css");
419
461
  }
@@ -427,8 +469,8 @@ async function buildPublicFolderSymlinks() {
427
469
  const splitNewPath = newPath.split("/");
428
470
  splitNewPath.pop();
429
471
  const newDir = splitNewPath.join("/");
430
- fs2.ensureDirSync(newDir);
431
- fs2.symlinkSync(file, newPath);
472
+ fs5.ensureDirSync(newDir);
473
+ fs5.symlinkSync(file, newPath);
432
474
  } catch {
433
475
  }
434
476
  }
@@ -439,7 +481,7 @@ async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
439
481
  if (!validExtensions.find((ext) => file.endsWith(ext))) return false;
440
482
  return {
441
483
  file,
442
- size: fs2.statSync(file).size / (1024 * 1e3)
484
+ size: fs5.statSync(file).size / (1024 * 1e3)
443
485
  };
444
486
  }).filter(Boolean);
445
487
  const totalSize = fileSizes.reduce((total, current) => {
@@ -447,6 +489,64 @@ async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
447
489
  }, 0);
448
490
  console.info(`[${totalSize.toFixed(3)} MB] Built ${type}`);
449
491
  }
492
+ async function buildStaticFiles(componentData) {
493
+ const componentsWithConfigs = componentData.filter((item) => item.configFile);
494
+ 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);
532
+ }
533
+ }
534
+ }
535
+ }
536
+ }
537
+ }
538
+ function sourcePathToDistPath(sourcePath) {
539
+ return sourcePath.replace(config.srcDir, config.distDir).replace(/\.ts$/, ".js").replace(/\.tsx$/, ".js");
540
+ }
541
+ function getComponentDynamicPaths(componentPath) {
542
+ const matches = [...componentPath.matchAll(regex_default.dynamicRoutes)];
543
+ if (!matches?.length) return null;
544
+ const output = [];
545
+ for (const match of matches) {
546
+ output.push(match[2]);
547
+ }
548
+ return output;
549
+ }
450
550
 
451
551
  // src/scripts/xpine-build.ts
452
552
  buildApp();