@absolutejs/absolute 0.5.0 → 0.5.1
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
CHANGED
|
@@ -1,4 +1,224 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
// src/constants.ts
|
|
3
|
+
var SECONDS_IN_A_MINUTE = 60;
|
|
4
|
+
var MILLISECONDS_IN_A_SECOND = 1000;
|
|
5
|
+
var MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
|
|
6
|
+
var MINUTES_IN_AN_HOUR = 60;
|
|
7
|
+
var HOURS_IN_A_DAY = 24;
|
|
8
|
+
var MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_A_DAY;
|
|
9
|
+
var TIME_PRECISION = 2;
|
|
10
|
+
var DEFAULT_PORT = 3000;
|
|
11
|
+
// src/core/build.ts
|
|
12
|
+
import { rm, mkdir, writeFile as writeFile2 } from "fs/promises";
|
|
13
|
+
import { join, basename } from "path";
|
|
14
|
+
import { cwd, exit } from "process";
|
|
15
|
+
var {$, build: bunBuild, Glob: Glob2 } = globalThis.Bun;
|
|
16
|
+
|
|
17
|
+
// src/utils/updateScriptTags.ts
|
|
18
|
+
import { readFile, writeFile } from "fs/promises";
|
|
19
|
+
var {Glob } = globalThis.Bun;
|
|
20
|
+
var updateScriptTags = async (manifest, htmlDir) => {
|
|
21
|
+
const htmlGlob = new Glob("*.html");
|
|
22
|
+
const htmlFiles = [];
|
|
23
|
+
for await (const file of htmlGlob.scan({
|
|
24
|
+
cwd: htmlDir,
|
|
25
|
+
absolute: true
|
|
26
|
+
})) {
|
|
27
|
+
htmlFiles.push(file);
|
|
28
|
+
}
|
|
29
|
+
for (const filePath of htmlFiles) {
|
|
30
|
+
let content = await readFile(filePath, "utf8");
|
|
31
|
+
for (const [scriptName, newPath] of Object.entries(manifest)) {
|
|
32
|
+
const escapedScriptName = scriptName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
33
|
+
const regex = new RegExp(`(<script[^>]+src=["'])(/?(?:.*\\/)?${escapedScriptName})(?:\\.[^."'/]+)?(\\.js)(["'][^>]*>)`, "g");
|
|
34
|
+
content = content.replace(regex, (_, prefix, _oldBase, _ext, suffix) => {
|
|
35
|
+
return `${prefix}${newPath}${suffix}`;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
await writeFile(filePath, content, "utf8");
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// src/core/build.ts
|
|
43
|
+
var build = async ({
|
|
44
|
+
buildDirectory = "build",
|
|
45
|
+
assetsDirectory,
|
|
46
|
+
reactDirectory,
|
|
47
|
+
html,
|
|
48
|
+
htmxDirectory,
|
|
49
|
+
tailwind
|
|
50
|
+
}) => {
|
|
51
|
+
const start = performance.now();
|
|
52
|
+
const projectRoot = cwd();
|
|
53
|
+
const buildDirAbsolute = join(projectRoot, buildDirectory);
|
|
54
|
+
const assetsDirAbsolute = assetsDirectory && join(projectRoot, assetsDirectory);
|
|
55
|
+
const reactIndexDirAbsolute = reactDirectory && join(projectRoot, reactDirectory, "indexes");
|
|
56
|
+
const reactPagesDirAbsolute = reactDirectory && join(projectRoot, reactDirectory, "pages");
|
|
57
|
+
const htmlDirAbsolute = html?.directory ? join(projectRoot, html.directory) : undefined;
|
|
58
|
+
const htmxDirAbsolute = htmxDirectory && join(projectRoot, htmxDirectory);
|
|
59
|
+
await rm(buildDirAbsolute, { force: true, recursive: true });
|
|
60
|
+
await mkdir(buildDirAbsolute);
|
|
61
|
+
reactPagesDirAbsolute && reactIndexDirAbsolute && await generateReactIndexFiles(reactPagesDirAbsolute, reactIndexDirAbsolute);
|
|
62
|
+
const reactEntryPaths = reactIndexDirAbsolute && await scanEntryPoints(reactIndexDirAbsolute, "*.tsx");
|
|
63
|
+
const entryPaths = [...reactEntryPaths ?? []];
|
|
64
|
+
if (entryPaths.length === 0) {
|
|
65
|
+
console.warn("No entry points found, skipping build");
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const { logs, outputs } = await bunBuild({
|
|
69
|
+
entrypoints: entryPaths,
|
|
70
|
+
format: "esm",
|
|
71
|
+
naming: `[dir]/[name].[hash].[ext]`,
|
|
72
|
+
outdir: buildDirAbsolute,
|
|
73
|
+
target: "bun"
|
|
74
|
+
}).catch((error) => {
|
|
75
|
+
console.error("Build failed:", error);
|
|
76
|
+
exit(1);
|
|
77
|
+
});
|
|
78
|
+
logs.forEach((log) => {
|
|
79
|
+
if (log.level === "error")
|
|
80
|
+
console.error(log);
|
|
81
|
+
else if (log.level === "warning")
|
|
82
|
+
console.warn(log);
|
|
83
|
+
else if (log.level === "info" || log.level === "debug")
|
|
84
|
+
console.info(log);
|
|
85
|
+
});
|
|
86
|
+
assetsDirAbsolute && await $`cp -R ${assetsDirAbsolute} ${buildDirAbsolute}`;
|
|
87
|
+
if (htmlDirAbsolute) {
|
|
88
|
+
await mkdir(join(buildDirAbsolute, "html"));
|
|
89
|
+
await $`cp -R ${htmlDirAbsolute} ${join(buildDirAbsolute)}`;
|
|
90
|
+
}
|
|
91
|
+
if (htmxDirAbsolute) {
|
|
92
|
+
await mkdir(join(buildDirAbsolute, "htmx"));
|
|
93
|
+
await $`cp -R ${htmxDirAbsolute} ${join(buildDirAbsolute)}`;
|
|
94
|
+
}
|
|
95
|
+
if (tailwind) {
|
|
96
|
+
await $`tailwindcss -i ${tailwind.input} -o ${join(buildDirAbsolute, tailwind.output)}`;
|
|
97
|
+
}
|
|
98
|
+
const manifest = outputs.reduce((acc, artifact) => {
|
|
99
|
+
let relativePath = artifact.path;
|
|
100
|
+
if (relativePath.startsWith(buildDirAbsolute)) {
|
|
101
|
+
relativePath = relativePath.slice(buildDirAbsolute.length);
|
|
102
|
+
}
|
|
103
|
+
relativePath = relativePath.replace(/^\/+/, "");
|
|
104
|
+
const baseName = relativePath.split("/").pop();
|
|
105
|
+
if (!baseName)
|
|
106
|
+
return acc;
|
|
107
|
+
const hashDelimiter = `.${artifact.hash}.`;
|
|
108
|
+
if (!baseName.includes(hashDelimiter)) {
|
|
109
|
+
throw new Error(`Expected hash delimiter ${hashDelimiter} in ${baseName}`);
|
|
110
|
+
}
|
|
111
|
+
const [fileName] = baseName.split(hashDelimiter);
|
|
112
|
+
acc[fileName] = "/" + relativePath;
|
|
113
|
+
return acc;
|
|
114
|
+
}, {});
|
|
115
|
+
htmlDirAbsolute && await updateScriptTags(manifest, htmlDirAbsolute);
|
|
116
|
+
const end = performance.now();
|
|
117
|
+
const durationMs = end - start;
|
|
118
|
+
let duration;
|
|
119
|
+
if (durationMs < MILLISECONDS_IN_A_SECOND) {
|
|
120
|
+
duration = `${durationMs.toFixed(TIME_PRECISION)}ms`;
|
|
121
|
+
} else if (durationMs < MILLISECONDS_IN_A_MINUTE) {
|
|
122
|
+
duration = `${(durationMs / MILLISECONDS_IN_A_SECOND).toFixed(TIME_PRECISION)}s`;
|
|
123
|
+
} else {
|
|
124
|
+
duration = `${(durationMs / MILLISECONDS_IN_A_MINUTE).toFixed(TIME_PRECISION)}m`;
|
|
125
|
+
}
|
|
126
|
+
console.log(`Build completed in ${duration}`);
|
|
127
|
+
return manifest;
|
|
128
|
+
};
|
|
129
|
+
var generateReactIndexFiles = async (reactPagesDirAbsolute, reactIndexDirAbsolute) => {
|
|
130
|
+
await rm(reactIndexDirAbsolute, { force: true, recursive: true });
|
|
131
|
+
await mkdir(reactIndexDirAbsolute);
|
|
132
|
+
const pagesGlob = new Glob2("*.*");
|
|
133
|
+
const files = [];
|
|
134
|
+
for await (const file of pagesGlob.scan({ cwd: reactPagesDirAbsolute })) {
|
|
135
|
+
files.push(file);
|
|
136
|
+
}
|
|
137
|
+
const promises = files.map(async (file) => {
|
|
138
|
+
const fileName = basename(file);
|
|
139
|
+
const [componentName] = fileName.split(".");
|
|
140
|
+
const content = [
|
|
141
|
+
`import { hydrateRoot } from 'react-dom/client';`,
|
|
142
|
+
`import { ${componentName} } from '../pages/${componentName}';
|
|
143
|
+
`,
|
|
144
|
+
`hydrateRoot(document, <${componentName} />);`
|
|
145
|
+
].join(`
|
|
146
|
+
`);
|
|
147
|
+
return writeFile2(join(reactIndexDirAbsolute, `${componentName}Index.tsx`), content);
|
|
148
|
+
});
|
|
149
|
+
await Promise.all(promises);
|
|
150
|
+
};
|
|
151
|
+
var scanEntryPoints = async (dir, pattern) => {
|
|
152
|
+
const entryPaths = [];
|
|
153
|
+
const glob = new Glob2(pattern);
|
|
154
|
+
for await (const file of glob.scan({ absolute: true, cwd: dir })) {
|
|
155
|
+
entryPaths.push(file);
|
|
156
|
+
}
|
|
157
|
+
return entryPaths;
|
|
158
|
+
};
|
|
159
|
+
// src/core/pageHandlers.ts
|
|
160
|
+
import { createElement } from "react";
|
|
161
|
+
import { renderToReadableStream } from "react-dom/server.browser";
|
|
162
|
+
var handleReactPageRequest = async (pageComponent, index) => {
|
|
163
|
+
const page = createElement(pageComponent);
|
|
164
|
+
const stream = await renderToReadableStream(page, {
|
|
165
|
+
bootstrapModules: [index]
|
|
166
|
+
});
|
|
167
|
+
return new Response(stream, {
|
|
168
|
+
headers: { "Content-Type": "text/html" }
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
var handleHTMLPageRequest = (html) => Bun.file(html);
|
|
172
|
+
// src/plugins/networkingPlugin.ts
|
|
173
|
+
import { argv } from "process";
|
|
174
|
+
var {env } = globalThis.Bun;
|
|
175
|
+
|
|
176
|
+
// src/utils/networking.ts
|
|
177
|
+
import os from "os";
|
|
178
|
+
var getLocalIPAddress = () => {
|
|
179
|
+
const interfaces = os.networkInterfaces();
|
|
180
|
+
const addresses = Object.values(interfaces).flat().filter((iface) => iface !== undefined);
|
|
181
|
+
const ipAddress = addresses.find((iface) => iface.family === "IPv4" && !iface.internal);
|
|
182
|
+
if (ipAddress)
|
|
183
|
+
return ipAddress.address;
|
|
184
|
+
console.warn("No IP address found, falling back to localhost");
|
|
185
|
+
return "localhost";
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// src/plugins/networkingPlugin.ts
|
|
189
|
+
var host = env.HOST ?? "localhost";
|
|
190
|
+
var port = env.PORT ?? DEFAULT_PORT;
|
|
191
|
+
var localIP;
|
|
192
|
+
var args = argv;
|
|
193
|
+
var hostFlag = args.includes("--host");
|
|
194
|
+
if (hostFlag) {
|
|
195
|
+
localIP = getLocalIPAddress();
|
|
196
|
+
host = "0.0.0.0";
|
|
197
|
+
}
|
|
198
|
+
var networkingPlugin = (app) => app.listen({
|
|
199
|
+
hostname: host,
|
|
200
|
+
port
|
|
201
|
+
}, () => {
|
|
202
|
+
if (hostFlag) {
|
|
203
|
+
console.log(`Server started on http://localhost:${port}`);
|
|
204
|
+
console.log(`Server started on network: http://${localIP}:${port}`);
|
|
205
|
+
} else {
|
|
206
|
+
console.log(`Server started on http://${host}:${port}`);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
export {
|
|
210
|
+
updateScriptTags,
|
|
211
|
+
networkingPlugin,
|
|
212
|
+
handleReactPageRequest,
|
|
213
|
+
handleHTMLPageRequest,
|
|
214
|
+
getLocalIPAddress,
|
|
215
|
+
build,
|
|
216
|
+
TIME_PRECISION,
|
|
217
|
+
SECONDS_IN_A_MINUTE,
|
|
218
|
+
MINUTES_IN_AN_HOUR,
|
|
219
|
+
MILLISECONDS_IN_A_SECOND,
|
|
220
|
+
MILLISECONDS_IN_A_MINUTE,
|
|
221
|
+
MILLISECONDS_IN_A_DAY,
|
|
222
|
+
HOURS_IN_A_DAY,
|
|
223
|
+
DEFAULT_PORT
|
|
224
|
+
};
|
package/dist/src/core/build.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { BuildConfig } from "
|
|
1
|
+
import { BuildConfig } from "../types";
|
|
2
2
|
export declare const build: ({ buildDirectory, assetsDirectory, reactDirectory, html, htmxDirectory, tailwind }: BuildConfig) => Promise<Record<string, string> | null>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/absolute",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "A fullstack meta-framework for building web applications with TypeScript",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"license": "CC BY-NC 4.0",
|
|
12
12
|
"author": "Alex Kahn",
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "rm -rf dist && bun build src/index.ts --outdir dist --
|
|
14
|
+
"build": "rm -rf dist && bun build src/index.ts --outdir dist --target=bun --external react --external react-dom --external elysia && tsc --emitDeclarationOnly --project tsconfig.json",
|
|
15
15
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
16
16
|
"format": "prettier --write \"./**/*.{js,jsx,ts,tsx,css,json}\"",
|
|
17
17
|
"dev": "bun run --watch example/server.ts",
|
package/src/core/build.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
TIME_PRECISION
|
|
9
9
|
} from "../constants";
|
|
10
10
|
import { updateScriptTags } from "../utils/updateScriptTags";
|
|
11
|
-
import { BuildConfig } from "
|
|
11
|
+
import { BuildConfig } from "../types";
|
|
12
12
|
|
|
13
13
|
export const build = async ({
|
|
14
14
|
buildDirectory = "build",
|
|
File without changes
|
|
File without changes
|