@aklinker1/aframe 0.4.12 → 0.5.0
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/README.md +1 -1
- package/bin/aframe.ts +1 -1
- package/package.json +1 -1
- package/src/client/server.ts +45 -49
- package/src/config.ts +4 -7
- package/src/env.d.ts +0 -7
- package/src/index.ts +82 -18
- package/src/prerenderer.ts +9 -6
package/README.md
CHANGED
package/bin/aframe.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { build, createServer } from "../src";
|
|
2
2
|
import { resolveConfig } from "../src/config";
|
|
3
|
-
import { RESET, BOLD,
|
|
3
|
+
import { RESET, BOLD, GREEN, CYAN } from "../src/color";
|
|
4
4
|
import { createTimer } from "../src/timer";
|
|
5
5
|
|
|
6
6
|
const [_bun, _aframe, ...args] = process.argv;
|
package/package.json
CHANGED
package/src/client/server.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { BunFile } from "bun";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
const headers = {
|
|
5
|
-
"Cache-Control": "max-age=31536000",
|
|
6
|
-
};
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join, extname, basename } from "node:path";
|
|
7
4
|
|
|
8
5
|
export interface AframeServer {
|
|
9
6
|
listen(port: number): void | never;
|
|
10
7
|
}
|
|
11
8
|
|
|
12
|
-
const
|
|
9
|
+
const staticPathsFile = join(import.meta.dir, "static.json");
|
|
10
|
+
const publicDir = join(import.meta.dir, "public");
|
|
11
|
+
|
|
12
|
+
let staticPaths: Record<string, { cacheable: boolean; path: string }> = {};
|
|
13
|
+
try {
|
|
14
|
+
staticPaths = JSON.parse(readFileSync(staticPathsFile, "utf-8"));
|
|
15
|
+
} catch {}
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* Fetches a file from the `public` directory.
|
|
@@ -22,48 +25,50 @@ export function fetchStatic(options?: {
|
|
|
22
25
|
) => Promise<Response | undefined> | Response | undefined;
|
|
23
26
|
}): (request: Request) => Promise<Response> {
|
|
24
27
|
return async (request) => {
|
|
25
|
-
const path = new URL(request.url).pathname.replace(/\/+$/, "");
|
|
26
|
-
|
|
27
|
-
const paths = [`${publicDir}${path}`, `${publicDir}${path}/index.html`];
|
|
28
|
-
|
|
29
|
-
// Only fallback on the root HTML file when building application
|
|
30
|
-
if (import.meta.command === "build") {
|
|
31
|
-
paths.push(`${publicDir}/index.html`);
|
|
32
|
-
}
|
|
28
|
+
const path = new URL(request.url).pathname.replace(/\/+$/, "") || "/";
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const file = Bun.file(
|
|
30
|
+
// Fetch file on disk
|
|
31
|
+
if (staticPaths[path]) {
|
|
32
|
+
const filePath = join(import.meta.dir, staticPaths[path].path);
|
|
33
|
+
const file = Bun.file(filePath);
|
|
34
|
+
const gzFile = Bun.file(filePath + ".gz");
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (customResponse) return customResponse;
|
|
42
|
-
return new Response(gzFile.stream(), {
|
|
43
|
-
headers: {
|
|
44
|
-
...(isHtml ? {} : headers),
|
|
45
|
-
"content-type": file.type,
|
|
46
|
-
"content-encoding": "gzip",
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
}
|
|
36
|
+
const customResponse = await options?.onFetch?.(path, file);
|
|
37
|
+
if (customResponse) return customResponse;
|
|
50
38
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
return new Response(gzFile.stream(), {
|
|
40
|
+
headers: {
|
|
41
|
+
"Content-Type": file.type,
|
|
42
|
+
"Content-Encoding": "gzip",
|
|
43
|
+
"Cache-Control": "max-age=31536000",
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
54
47
|
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
const ext = extname(basename(path));
|
|
49
|
+
if (ext) {
|
|
50
|
+
return new Response(undefined, { status: 404 });
|
|
57
51
|
}
|
|
58
52
|
|
|
53
|
+
// Fallback to public/index.html file
|
|
54
|
+
if (import.meta.command === "build") {
|
|
55
|
+
const file = Bun.file(join(publicDir, "index.html"));
|
|
56
|
+
const gzFile = Bun.file(join(publicDir, "index.html.gz"));
|
|
57
|
+
return new Response(gzFile.stream(), {
|
|
58
|
+
headers: {
|
|
59
|
+
"Content-Type": file.type,
|
|
60
|
+
"Content-Encoding": "gzip",
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}
|
|
59
64
|
return new Response(
|
|
60
65
|
`<html>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
<body>
|
|
67
|
+
This is a placeholder for your root <code>index.html</code> file during development.
|
|
68
|
+
<br/>
|
|
69
|
+
In production (or via the app's dev server), this path will fallback on the root <code>index.html</code>.
|
|
70
|
+
</body>
|
|
71
|
+
</html>`,
|
|
67
72
|
{
|
|
68
73
|
headers: {
|
|
69
74
|
"Content-Type": "text/html",
|
|
@@ -72,12 +77,3 @@ export function fetchStatic(options?: {
|
|
|
72
77
|
);
|
|
73
78
|
};
|
|
74
79
|
}
|
|
75
|
-
|
|
76
|
-
async function isFile(file: BunFile): Promise<boolean> {
|
|
77
|
-
try {
|
|
78
|
-
const stats = await file.stat();
|
|
79
|
-
return stats.isFile();
|
|
80
|
-
} catch {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
}
|
package/src/config.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as vite from "vite";
|
|
2
2
|
import { resolve, join, relative } from "node:path/posix";
|
|
3
|
-
import { mkdir } from "node:fs/promises";
|
|
4
3
|
import type { LaunchOptions } from "puppeteer";
|
|
5
4
|
|
|
6
5
|
export type UserConfig = {
|
|
@@ -39,7 +38,7 @@ export type ResolvedConfig = {
|
|
|
39
38
|
serverDir: string;
|
|
40
39
|
serverModule: string;
|
|
41
40
|
serverEntry: string;
|
|
42
|
-
|
|
41
|
+
prerenderedDir: string;
|
|
43
42
|
proxyPaths: string[];
|
|
44
43
|
outDir: string;
|
|
45
44
|
serverOutDir: string;
|
|
@@ -69,10 +68,7 @@ export async function resolveConfig(
|
|
|
69
68
|
const outDir = join(rootDir, ".output");
|
|
70
69
|
const appOutDir = join(outDir, "public");
|
|
71
70
|
const serverOutDir = outDir;
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
// Ensure required directories exist
|
|
75
|
-
await mkdir(prerenderToDir, { recursive: true });
|
|
71
|
+
const prerenderedDir = join(outDir, "prerendered");
|
|
76
72
|
|
|
77
73
|
const configFile = join(rootDir, "aframe.config"); // No file extension to resolve any JS/TS file
|
|
78
74
|
const relativeConfigFile = "./" + relative(import.meta.dir, configFile);
|
|
@@ -120,6 +116,7 @@ export async function resolveConfig(
|
|
|
120
116
|
publicDir,
|
|
121
117
|
envDir: rootDir,
|
|
122
118
|
build: {
|
|
119
|
+
emptyOutDir: false,
|
|
123
120
|
outDir: appOutDir,
|
|
124
121
|
},
|
|
125
122
|
server: {
|
|
@@ -140,7 +137,7 @@ export async function resolveConfig(
|
|
|
140
137
|
outDir,
|
|
141
138
|
serverOutDir,
|
|
142
139
|
appOutDir,
|
|
143
|
-
|
|
140
|
+
prerenderedDir,
|
|
144
141
|
appPort,
|
|
145
142
|
serverPort,
|
|
146
143
|
proxyPaths,
|
package/src/env.d.ts
CHANGED
|
@@ -2,13 +2,6 @@ import "vite/client";
|
|
|
2
2
|
|
|
3
3
|
declare global {
|
|
4
4
|
interface ImportMeta {
|
|
5
|
-
/**
|
|
6
|
-
* Absolute path or relative path (relative to main server file, not CWD).
|
|
7
|
-
* This ensures the public directory path is constant regardless of the CWD.
|
|
8
|
-
* It allows dev mode, production builds, and preview mode to all run from
|
|
9
|
-
* any working directory.
|
|
10
|
-
*/
|
|
11
|
-
publicDir: string;
|
|
12
5
|
command: string;
|
|
13
6
|
}
|
|
14
7
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { BunPlugin } from "bun";
|
|
2
|
-
import { lstatSync } from "node:fs";
|
|
3
|
-
import { mkdir, rm } from "node:fs/promises";
|
|
2
|
+
import { createReadStream, createWriteStream, lstatSync } from "node:fs";
|
|
3
|
+
import { mkdir, readdir, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { join, relative } from "node:path/posix";
|
|
5
5
|
import * as vite from "vite";
|
|
6
6
|
import { BLUE, BOLD, CYAN, DIM, GREEN, MAGENTA, RESET } from "./color";
|
|
7
7
|
import type { ResolvedConfig } from "./config";
|
|
8
8
|
import { createTimer } from "./timer";
|
|
9
9
|
import { prerenderPages, type PrerenderedRoute } from "./prerenderer";
|
|
10
|
+
import { createGzip } from "node:zlib";
|
|
11
|
+
import { pipeline } from "node:stream/promises";
|
|
10
12
|
|
|
11
13
|
export * from "./config";
|
|
12
14
|
export * from "./dev-server";
|
|
@@ -22,25 +24,55 @@ export async function build(config: ResolvedConfig) {
|
|
|
22
24
|
console.log(
|
|
23
25
|
`${BOLD}${CYAN}ℹ${RESET} Building ${CYAN}./app${RESET} with ${GREEN}Vite ${vite.version}${RESET}`,
|
|
24
26
|
);
|
|
25
|
-
const
|
|
26
|
-
config.vite,
|
|
27
|
-
)) as vite.Rollup.RollupOutput;
|
|
27
|
+
const appOutput = (await vite.build(config.vite)) as vite.Rollup.RollupOutput;
|
|
28
28
|
console.log(`${GREEN}✔${RESET} Built in ${appTimer()}`);
|
|
29
29
|
|
|
30
|
+
const allAbsoluteAppFiles = (
|
|
31
|
+
await readdir(config.appOutDir, { recursive: true, withFileTypes: true })
|
|
32
|
+
)
|
|
33
|
+
.filter((entry) => entry.isFile())
|
|
34
|
+
.map((entry) => join(entry.parentPath, entry.name));
|
|
35
|
+
const allAppFiles = allAbsoluteAppFiles.map((path) =>
|
|
36
|
+
relative(config.appOutDir, path),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const bundledAppFiles = appOutput.output.map((entry) => entry.fileName);
|
|
40
|
+
const bundledAppFileSet = new Set(bundledAppFiles);
|
|
41
|
+
const publicAppFiles = allAppFiles.filter(
|
|
42
|
+
(file) => !bundledAppFileSet.has(file),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
await gzipFiles(config, allAbsoluteAppFiles);
|
|
46
|
+
|
|
47
|
+
const staticRoutesFile = join(config.serverOutDir, "static.json");
|
|
48
|
+
let staticRoutes = [
|
|
49
|
+
...Array.from(bundledAppFiles)
|
|
50
|
+
.filter((path) => path !== "index.html")
|
|
51
|
+
.map((path) => [`/${path}`, { cacheable: true, path: `public/${path}` }]),
|
|
52
|
+
...publicAppFiles
|
|
53
|
+
.filter((path) => path !== "index.html")
|
|
54
|
+
.map((path) => [
|
|
55
|
+
`/${path}`,
|
|
56
|
+
{ cacheable: false, path: `public/${path}` },
|
|
57
|
+
]),
|
|
58
|
+
];
|
|
59
|
+
await writeFile(
|
|
60
|
+
staticRoutesFile,
|
|
61
|
+
JSON.stringify(Object.fromEntries(staticRoutes)),
|
|
62
|
+
);
|
|
63
|
+
|
|
30
64
|
console.log();
|
|
31
65
|
|
|
32
66
|
const serverTimer = createTimer();
|
|
33
67
|
console.log(
|
|
34
68
|
`${BOLD}${CYAN}ℹ${RESET} Building ${CYAN}./server${RESET} with ${MAGENTA}Bun ${Bun.version}${RESET}`,
|
|
35
69
|
);
|
|
36
|
-
|
|
70
|
+
await Bun.build({
|
|
37
71
|
outdir: config.serverOutDir,
|
|
38
72
|
sourcemap: "external",
|
|
39
73
|
entrypoints: [config.serverEntry],
|
|
40
74
|
target: "bun",
|
|
41
75
|
define: {
|
|
42
|
-
// In production, the public directory is inside the CWD
|
|
43
|
-
"import.meta.publicDir": `"public"`,
|
|
44
76
|
"import.meta.command": `"build"`,
|
|
45
77
|
},
|
|
46
78
|
plugins: [aframeServerMainBunPlugin(config)],
|
|
@@ -65,20 +97,36 @@ export async function build(config: ResolvedConfig) {
|
|
|
65
97
|
console.log(`${DIM}${BOLD}→${RESET} Pre-rendering disabled`);
|
|
66
98
|
}
|
|
67
99
|
|
|
100
|
+
await gzipFiles(
|
|
101
|
+
config,
|
|
102
|
+
prerendered.map((entry) => entry.absolutePath),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
staticRoutes = staticRoutes.concat(
|
|
106
|
+
prerendered.map((entry) => [
|
|
107
|
+
entry.route,
|
|
108
|
+
{ cacheable: false, path: `prerendered/${entry.relativePath}` },
|
|
109
|
+
]),
|
|
110
|
+
);
|
|
111
|
+
await writeFile(
|
|
112
|
+
staticRoutesFile,
|
|
113
|
+
JSON.stringify(Object.fromEntries(staticRoutes)),
|
|
114
|
+
);
|
|
115
|
+
|
|
68
116
|
console.log();
|
|
69
117
|
|
|
70
118
|
console.log(`${GREEN}✔${RESET} Application built in ${buildTimer()}`);
|
|
71
119
|
const relativeOutDir = `${relative(config.rootDir, config.outDir)}/`;
|
|
72
|
-
const files =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
120
|
+
const files = (
|
|
121
|
+
await readdir(config.outDir, { recursive: true, withFileTypes: true })
|
|
122
|
+
)
|
|
123
|
+
.filter((entry) => entry.isFile())
|
|
124
|
+
.map((entry) => join(entry.parentPath, entry.name))
|
|
125
|
+
.toSorted()
|
|
126
|
+
.map((file): [file: string, size: number] => [
|
|
127
|
+
relative(config.outDir, file),
|
|
128
|
+
lstatSync(file).size,
|
|
129
|
+
]);
|
|
82
130
|
const fileColumnCount = files.reduce(
|
|
83
131
|
(max, [file]) => Math.max(file.length, max),
|
|
84
132
|
0,
|
|
@@ -125,3 +173,19 @@ function prettyBytes(bytes: number) {
|
|
|
125
173
|
const value = bytes / Math.pow(base, exponent);
|
|
126
174
|
return `${unit === "B" ? value : value.toFixed(2)} ${unit}`;
|
|
127
175
|
}
|
|
176
|
+
|
|
177
|
+
async function gzipFiles(
|
|
178
|
+
config: ResolvedConfig,
|
|
179
|
+
files: string[],
|
|
180
|
+
): Promise<void> {
|
|
181
|
+
for (const file of files) await gzipFile(config, file);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function gzipFile(config: ResolvedConfig, file: string): Promise<void> {
|
|
185
|
+
await writeFile(`${file}.gz`, "");
|
|
186
|
+
await pipeline(
|
|
187
|
+
createReadStream(file),
|
|
188
|
+
createGzip(),
|
|
189
|
+
createWriteStream(`${file}.gz`),
|
|
190
|
+
);
|
|
191
|
+
}
|
package/src/prerenderer.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {} from "node:url";
|
|
2
|
-
import { join } from "node:path";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
3
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
4
|
import type { Browser } from "puppeteer";
|
|
5
5
|
import type { ResolvedConfig } from "./config";
|
|
6
6
|
|
|
7
7
|
export type PrerenderedRoute = {
|
|
8
8
|
route: string;
|
|
9
|
-
|
|
9
|
+
absolutePath: string;
|
|
10
|
+
relativePath: string;
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
export async function prerenderPages(
|
|
@@ -67,13 +68,15 @@ export async function prerenderPages(
|
|
|
67
68
|
throw Error("Vite error prevented page from being rendered.");
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
const
|
|
71
|
-
const
|
|
71
|
+
const relativePath = join(route.substring(1), "index.html");
|
|
72
|
+
const absolutePath = join(config.prerenderedDir, relativePath);
|
|
73
|
+
const dir = dirname(absolutePath);
|
|
72
74
|
await mkdir(dir, { recursive: true });
|
|
73
|
-
await writeFile(
|
|
75
|
+
await writeFile(absolutePath, html);
|
|
74
76
|
results.push({
|
|
75
|
-
file,
|
|
76
77
|
route,
|
|
78
|
+
relativePath,
|
|
79
|
+
absolutePath,
|
|
77
80
|
});
|
|
78
81
|
}
|
|
79
82
|
} finally {
|