xpine 0.0.21 → 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.
- package/TODO +0 -2
- package/dist/index.js +184 -80
- package/dist/src/build/esbuild/addDotJS.d.ts +5 -0
- package/dist/src/build/esbuild/addDotJS.d.ts.map +1 -0
- package/dist/src/build/esbuild/getDataFiles.d.ts +5 -0
- package/dist/src/build/esbuild/getDataFiles.d.ts.map +1 -0
- package/dist/src/build/esbuild/transformTSXFiles.d.ts +6 -0
- package/dist/src/build/esbuild/transformTSXFiles.d.ts.map +1 -0
- package/dist/src/express.d.ts +3 -0
- package/dist/src/express.d.ts.map +1 -1
- package/dist/src/runDevServer.d.ts.map +1 -1
- package/dist/src/scripts/build.d.ts +5 -1
- package/dist/src/scripts/build.d.ts.map +1 -1
- package/dist/src/scripts/xpine-build.js +110 -65
- package/dist/src/scripts/xpine-dev.js +148 -68
- package/dist/src/static/spa.js +2 -1
- package/dist/src/util/config-file.d.ts +6 -0
- package/dist/src/util/config-file.d.ts.map +1 -0
- package/dist/src/util/constants.d.ts +3 -0
- package/dist/src/util/constants.d.ts.map +1 -0
- package/dist/src/util/get-config.d.ts.map +1 -1
- package/dist/src/util/postcss/remove-layers.d.ts +1 -1
- package/dist/src/util/regex.d.ts +11 -0
- package/dist/src/util/regex.d.ts.map +1 -0
- package/dist/src/util/require.d.ts.map +1 -1
- package/dist/types.d.ts +31 -2
- package/dist/types.d.ts.map +1 -1
- package/eslint.config.mjs +27 -0
- package/package.json +18 -4
- package/tests/e2e/alpine.spec.ts +14 -0
- package/tests/e2e/app-build.spec.ts +73 -0
- package/tests/e2e/spa.spec.ts +23 -0
- package/tests/e2e/tests-examples/demo-todo-app.spec.ts +437 -0
- package/tests/eslint.config.mjs +27 -0
- package/tests/package-lock.json +8941 -0
- package/tests/package.json +89 -0
- package/tests/playwright.config.ts +81 -0
- package/tests/tsconfig.json +46 -0
- package/tests/xpine.config.mjs +1 -0
- 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]
|
|
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
|
-
|
|
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
|
|
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
|
|
493
|
-
const
|
|
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
|
-
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
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
|
-
|
|
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]
|
|
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
|
-
|
|
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
|
|
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
|
|
500
|
-
const
|
|
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
|
-
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
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
|
|
608
|
-
|
|
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();
|
package/dist/src/static/spa.js
CHANGED
|
@@ -6,7 +6,8 @@ async function replaceDocumentContentsWithLinkResponse() {
|
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
async function updatePageOnLinkClick(e) {
|
|
9
|
-
|
|
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 @@
|
|
|
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;
|
|
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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regex.d.ts","sourceRoot":"","sources":["../../../src/util/regex.ts"],"names":[],"mappings":";;;;;;;;;AAAA,wBAQE"}
|