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.
- package/dist/index.js +236 -111
- package/dist/src/scripts/xpine-build.js +200 -100
- package/dist/src/scripts/xpine-dev.js +207 -107
- package/package.json +1 -2
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/scripts/build.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import path5 from "path";
|
|
5
|
+
import fs5 from "fs-extra";
|
|
6
6
|
import { build } from "esbuild";
|
|
7
|
-
import
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
252
|
-
|
|
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 =
|
|
323
|
-
|
|
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 =
|
|
353
|
-
const content =
|
|
354
|
-
|
|
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 =
|
|
359
|
-
const content =
|
|
360
|
-
|
|
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 =
|
|
422
|
+
const sourceFile = ts3.createSourceFile(
|
|
381
423
|
component.path,
|
|
382
424
|
component.contents,
|
|
383
|
-
|
|
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
|
-
|
|
406
|
-
|
|
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 =
|
|
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
|
-
|
|
416
|
-
|
|
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
|
-
|
|
431
|
-
|
|
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:
|
|
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();
|