@float.js/core 2.0.7 → 2.2.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 +66 -0
- package/dist/cli/index.js +620 -135
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +630 -145
- package/dist/index.js.map +1 -1
- package/dist/router/index.d.ts +4 -0
- package/dist/router/index.js +32 -2
- package/dist/router/index.js.map +1 -1
- package/dist/server/index.js +557 -72
- package/dist/server/index.js.map +1 -1
- package/package.json +12 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
3
9
|
var __esm = (fn, res) => function __init() {
|
|
4
10
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
11
|
};
|
|
@@ -729,7 +735,7 @@ function generateAPIResponse(state) {
|
|
|
729
735
|
function createDevDashboard(options = {}) {
|
|
730
736
|
const {
|
|
731
737
|
enabled = process.env.NODE_ENV !== "production",
|
|
732
|
-
path:
|
|
738
|
+
path: path10 = "/__float",
|
|
733
739
|
auth
|
|
734
740
|
} = options;
|
|
735
741
|
if (!enabled) {
|
|
@@ -737,7 +743,7 @@ function createDevDashboard(options = {}) {
|
|
|
737
743
|
}
|
|
738
744
|
return (req, res, next) => {
|
|
739
745
|
const url = req.url || "";
|
|
740
|
-
if (!url.startsWith(
|
|
746
|
+
if (!url.startsWith(path10)) {
|
|
741
747
|
return next();
|
|
742
748
|
}
|
|
743
749
|
if (auth) {
|
|
@@ -756,7 +762,7 @@ function createDevDashboard(options = {}) {
|
|
|
756
762
|
return;
|
|
757
763
|
}
|
|
758
764
|
}
|
|
759
|
-
const subPath = url.slice(
|
|
765
|
+
const subPath = url.slice(path10.length);
|
|
760
766
|
if (subPath === "" || subPath === "/") {
|
|
761
767
|
res.setHeader("Content-Type", "text/html");
|
|
762
768
|
res.end(generateDashboardHTML(dashboardState));
|
|
@@ -928,6 +934,26 @@ function findLayouts(routePath, allLayouts) {
|
|
|
928
934
|
}
|
|
929
935
|
return layouts;
|
|
930
936
|
}
|
|
937
|
+
function findLoading(routePath, allLoading) {
|
|
938
|
+
const segments = routePath.split("/").filter(Boolean);
|
|
939
|
+
for (let i = segments.length; i >= 0; i--) {
|
|
940
|
+
const currentPath = i === 0 ? "/" : "/" + segments.slice(0, i).join("/");
|
|
941
|
+
if (allLoading.has(currentPath)) {
|
|
942
|
+
return allLoading.get(currentPath);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
return void 0;
|
|
946
|
+
}
|
|
947
|
+
function findError(routePath, allErrors) {
|
|
948
|
+
const segments = routePath.split("/").filter(Boolean);
|
|
949
|
+
for (let i = segments.length; i >= 0; i--) {
|
|
950
|
+
const currentPath = i === 0 ? "/" : "/" + segments.slice(0, i).join("/");
|
|
951
|
+
if (allErrors.has(currentPath)) {
|
|
952
|
+
return allErrors.get(currentPath);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return void 0;
|
|
956
|
+
}
|
|
931
957
|
async function scanRoutes(rootDir, options = {}) {
|
|
932
958
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
933
959
|
const appDir = path2.join(rootDir, opts.appDir);
|
|
@@ -939,12 +965,20 @@ async function scanRoutes(rootDir, options = {}) {
|
|
|
939
965
|
ignore: ["**/node_modules/**", "**/_*/**"]
|
|
940
966
|
});
|
|
941
967
|
const layoutMap = /* @__PURE__ */ new Map();
|
|
968
|
+
const loadingMap = /* @__PURE__ */ new Map();
|
|
969
|
+
const errorMap = /* @__PURE__ */ new Map();
|
|
942
970
|
for (const file of files) {
|
|
943
971
|
const type = getRouteType(file);
|
|
972
|
+
const { urlPath } = filePathToUrlPath(file, "");
|
|
944
973
|
if (type === "layout") {
|
|
945
|
-
const { urlPath } = filePathToUrlPath(file, "");
|
|
946
974
|
const layoutPath = urlPath === "/" ? "/" : urlPath.replace(/\/layout$/, "") || "/";
|
|
947
975
|
layoutMap.set(layoutPath, path2.join(appDir, file));
|
|
976
|
+
} else if (type === "loading") {
|
|
977
|
+
const loadingPath = urlPath === "/" ? "/" : urlPath.replace(/\/loading$/, "") || "/";
|
|
978
|
+
loadingMap.set(loadingPath, path2.join(appDir, file));
|
|
979
|
+
} else if (type === "error") {
|
|
980
|
+
const errorPath = urlPath === "/" ? "/" : urlPath.replace(/\/error$/, "") || "/";
|
|
981
|
+
errorMap.set(errorPath, path2.join(appDir, file));
|
|
948
982
|
}
|
|
949
983
|
}
|
|
950
984
|
const routes = [];
|
|
@@ -962,7 +996,9 @@ async function scanRoutes(rootDir, options = {}) {
|
|
|
962
996
|
params,
|
|
963
997
|
isCatchAll,
|
|
964
998
|
isOptionalCatchAll,
|
|
965
|
-
layouts: type === "page" ? findLayouts(urlPath, layoutMap) : []
|
|
999
|
+
layouts: type === "page" ? findLayouts(urlPath, layoutMap) : [],
|
|
1000
|
+
loading: type === "page" ? findLoading(urlPath, loadingMap) : void 0,
|
|
1001
|
+
error: type === "page" ? findError(urlPath, errorMap) : void 0
|
|
966
1002
|
};
|
|
967
1003
|
routes.push(route);
|
|
968
1004
|
}
|
|
@@ -1017,9 +1053,9 @@ init_esm_shims();
|
|
|
1017
1053
|
// src/server/dev-server.ts
|
|
1018
1054
|
init_esm_shims();
|
|
1019
1055
|
import http from "http";
|
|
1020
|
-
import
|
|
1021
|
-
import
|
|
1022
|
-
import
|
|
1056
|
+
import fs5 from "fs";
|
|
1057
|
+
import path7 from "path";
|
|
1058
|
+
import pc2 from "picocolors";
|
|
1023
1059
|
import chokidar from "chokidar";
|
|
1024
1060
|
import { WebSocketServer, WebSocket } from "ws";
|
|
1025
1061
|
import mime from "mime-types";
|
|
@@ -1033,23 +1069,222 @@ import { Writable } from "stream";
|
|
|
1033
1069
|
// src/build/transform.ts
|
|
1034
1070
|
init_esm_shims();
|
|
1035
1071
|
import * as esbuild from "esbuild";
|
|
1072
|
+
import fs2 from "fs";
|
|
1073
|
+
import path4 from "path";
|
|
1074
|
+
import { pathToFileURL } from "url";
|
|
1075
|
+
|
|
1076
|
+
// src/build/persistent-cache.ts
|
|
1077
|
+
init_esm_shims();
|
|
1036
1078
|
import fs from "fs";
|
|
1037
1079
|
import path3 from "path";
|
|
1038
|
-
import
|
|
1080
|
+
import crypto from "crypto";
|
|
1081
|
+
var PersistentCache = class {
|
|
1082
|
+
cacheDir;
|
|
1083
|
+
manifestPath;
|
|
1084
|
+
manifest;
|
|
1085
|
+
constructor(rootDir = process.cwd()) {
|
|
1086
|
+
this.cacheDir = path3.join(rootDir, ".float", "cache");
|
|
1087
|
+
this.manifestPath = path3.join(this.cacheDir, "manifest.json");
|
|
1088
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
1089
|
+
this.manifest = this.loadManifest();
|
|
1090
|
+
}
|
|
1091
|
+
loadManifest() {
|
|
1092
|
+
if (fs.existsSync(this.manifestPath)) {
|
|
1093
|
+
try {
|
|
1094
|
+
const data = fs.readFileSync(this.manifestPath, "utf-8");
|
|
1095
|
+
return JSON.parse(data);
|
|
1096
|
+
} catch {
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
return {
|
|
1100
|
+
version: "1.0",
|
|
1101
|
+
entries: {}
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
saveManifest() {
|
|
1105
|
+
fs.writeFileSync(
|
|
1106
|
+
this.manifestPath,
|
|
1107
|
+
JSON.stringify(this.manifest, null, 2)
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Generate hash for content
|
|
1112
|
+
*/
|
|
1113
|
+
hash(content) {
|
|
1114
|
+
return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Get cache key path
|
|
1118
|
+
*/
|
|
1119
|
+
getKeyPath(key) {
|
|
1120
|
+
const safeKey = key.replace(/[^a-z0-9_-]/gi, "_");
|
|
1121
|
+
return path3.join(this.cacheDir, `${safeKey}.cache`);
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Check if cache entry is valid
|
|
1125
|
+
*/
|
|
1126
|
+
has(key, contentHash) {
|
|
1127
|
+
const entry = this.manifest.entries[key];
|
|
1128
|
+
if (!entry) return false;
|
|
1129
|
+
const cachePath = this.getKeyPath(key);
|
|
1130
|
+
if (!fs.existsSync(cachePath)) {
|
|
1131
|
+
delete this.manifest.entries[key];
|
|
1132
|
+
this.saveManifest();
|
|
1133
|
+
return false;
|
|
1134
|
+
}
|
|
1135
|
+
if (contentHash && entry.hash !== contentHash) {
|
|
1136
|
+
return false;
|
|
1137
|
+
}
|
|
1138
|
+
return true;
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Get cached value
|
|
1142
|
+
*/
|
|
1143
|
+
get(key) {
|
|
1144
|
+
if (!this.has(key)) return null;
|
|
1145
|
+
try {
|
|
1146
|
+
const cachePath = this.getKeyPath(key);
|
|
1147
|
+
const data = fs.readFileSync(cachePath, "utf-8");
|
|
1148
|
+
const entry = JSON.parse(data);
|
|
1149
|
+
return entry.value;
|
|
1150
|
+
} catch {
|
|
1151
|
+
return null;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Set cache value
|
|
1156
|
+
*/
|
|
1157
|
+
set(key, value, content) {
|
|
1158
|
+
const entry = {
|
|
1159
|
+
key,
|
|
1160
|
+
value,
|
|
1161
|
+
timestamp: Date.now(),
|
|
1162
|
+
hash: content ? this.hash(content) : this.hash(JSON.stringify(value))
|
|
1163
|
+
};
|
|
1164
|
+
const cachePath = this.getKeyPath(key);
|
|
1165
|
+
const data = JSON.stringify(entry);
|
|
1166
|
+
fs.writeFileSync(cachePath, data);
|
|
1167
|
+
this.manifest.entries[key] = {
|
|
1168
|
+
hash: entry.hash,
|
|
1169
|
+
timestamp: entry.timestamp,
|
|
1170
|
+
size: Buffer.byteLength(data)
|
|
1171
|
+
};
|
|
1172
|
+
this.saveManifest();
|
|
1173
|
+
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Delete cache entry
|
|
1176
|
+
*/
|
|
1177
|
+
delete(key) {
|
|
1178
|
+
const cachePath = this.getKeyPath(key);
|
|
1179
|
+
if (fs.existsSync(cachePath)) {
|
|
1180
|
+
fs.unlinkSync(cachePath);
|
|
1181
|
+
}
|
|
1182
|
+
if (this.manifest.entries[key]) {
|
|
1183
|
+
delete this.manifest.entries[key];
|
|
1184
|
+
this.saveManifest();
|
|
1185
|
+
return true;
|
|
1186
|
+
}
|
|
1187
|
+
return false;
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Clear all cache
|
|
1191
|
+
*/
|
|
1192
|
+
clear() {
|
|
1193
|
+
if (fs.existsSync(this.cacheDir)) {
|
|
1194
|
+
fs.rmSync(this.cacheDir, { recursive: true });
|
|
1195
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
1196
|
+
}
|
|
1197
|
+
this.manifest = {
|
|
1198
|
+
version: "1.0",
|
|
1199
|
+
entries: {}
|
|
1200
|
+
};
|
|
1201
|
+
this.saveManifest();
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Get cache statistics
|
|
1205
|
+
*/
|
|
1206
|
+
stats() {
|
|
1207
|
+
const entries = Object.values(this.manifest.entries);
|
|
1208
|
+
const size = entries.reduce((acc, e) => acc + e.size, 0);
|
|
1209
|
+
const timestamps = entries.map((e) => e.timestamp);
|
|
1210
|
+
return {
|
|
1211
|
+
entries: entries.length,
|
|
1212
|
+
size,
|
|
1213
|
+
oldestEntry: timestamps.length > 0 ? Math.min(...timestamps) : null,
|
|
1214
|
+
newestEntry: timestamps.length > 0 ? Math.max(...timestamps) : null
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Clean old entries (older than maxAge milliseconds)
|
|
1219
|
+
*/
|
|
1220
|
+
prune(maxAge = 7 * 24 * 60 * 60 * 1e3) {
|
|
1221
|
+
const now = Date.now();
|
|
1222
|
+
let cleaned = 0;
|
|
1223
|
+
for (const [key, entry] of Object.entries(this.manifest.entries)) {
|
|
1224
|
+
if (now - entry.timestamp > maxAge) {
|
|
1225
|
+
this.delete(key);
|
|
1226
|
+
cleaned++;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
return cleaned;
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
var globalCache = null;
|
|
1233
|
+
function getCache(rootDir) {
|
|
1234
|
+
if (!globalCache) {
|
|
1235
|
+
globalCache = new PersistentCache(rootDir);
|
|
1236
|
+
}
|
|
1237
|
+
return globalCache;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// src/build/transform.ts
|
|
1039
1241
|
var moduleCache = /* @__PURE__ */ new Map();
|
|
1040
|
-
async function transformFile(filePath) {
|
|
1041
|
-
const absolutePath =
|
|
1042
|
-
if (!
|
|
1242
|
+
async function transformFile(filePath, useCache = true) {
|
|
1243
|
+
const absolutePath = path4.isAbsolute(filePath) ? filePath : path4.resolve(filePath);
|
|
1244
|
+
if (!fs2.existsSync(absolutePath)) {
|
|
1043
1245
|
throw new Error(`File not found: ${absolutePath}`);
|
|
1044
1246
|
}
|
|
1045
|
-
const stats =
|
|
1247
|
+
const stats = fs2.statSync(absolutePath);
|
|
1046
1248
|
const mtime = stats.mtimeMs;
|
|
1047
1249
|
const cached = moduleCache.get(absolutePath);
|
|
1048
1250
|
if (cached && cached.mtime === mtime) {
|
|
1049
1251
|
return cached.module;
|
|
1050
1252
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
1253
|
+
if (useCache) {
|
|
1254
|
+
const cache2 = getCache();
|
|
1255
|
+
const source2 = fs2.readFileSync(absolutePath, "utf-8");
|
|
1256
|
+
const sourceHash = __require("crypto").createHash("sha256").update(source2).digest("hex").slice(0, 16);
|
|
1257
|
+
const cacheKey = `transform_${absolutePath}_${sourceHash}`;
|
|
1258
|
+
if (cache2.has(cacheKey)) {
|
|
1259
|
+
const cachedCode = cache2.get(cacheKey);
|
|
1260
|
+
if (cachedCode) {
|
|
1261
|
+
const tempDir2 = path4.join(process.cwd(), ".float", ".cache");
|
|
1262
|
+
fs2.mkdirSync(tempDir2, { recursive: true });
|
|
1263
|
+
const tempFile2 = path4.join(tempDir2, `${path4.basename(absolutePath, path4.extname(absolutePath))}_${Date.now()}.mjs`);
|
|
1264
|
+
fs2.writeFileSync(tempFile2, cachedCode);
|
|
1265
|
+
try {
|
|
1266
|
+
const module = await import(pathToFileURL(tempFile2).href);
|
|
1267
|
+
moduleCache.set(absolutePath, { module, mtime });
|
|
1268
|
+
setImmediate(() => {
|
|
1269
|
+
try {
|
|
1270
|
+
fs2.unlinkSync(tempFile2);
|
|
1271
|
+
} catch {
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
return module;
|
|
1275
|
+
} catch (error2) {
|
|
1276
|
+
setImmediate(() => {
|
|
1277
|
+
try {
|
|
1278
|
+
fs2.unlinkSync(tempFile2);
|
|
1279
|
+
} catch {
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
const source = fs2.readFileSync(absolutePath, "utf-8");
|
|
1287
|
+
const ext = path4.extname(absolutePath);
|
|
1053
1288
|
const loader = getLoader(ext);
|
|
1054
1289
|
const result = await esbuild.transform(source, {
|
|
1055
1290
|
loader,
|
|
@@ -1059,25 +1294,31 @@ async function transformFile(filePath) {
|
|
|
1059
1294
|
sourcemap: "inline",
|
|
1060
1295
|
sourcefile: absolutePath
|
|
1061
1296
|
});
|
|
1062
|
-
const tempDir =
|
|
1063
|
-
|
|
1064
|
-
const tempFile =
|
|
1297
|
+
const tempDir = path4.join(process.cwd(), ".float", ".cache");
|
|
1298
|
+
fs2.mkdirSync(tempDir, { recursive: true });
|
|
1299
|
+
const tempFile = path4.join(tempDir, `${path4.basename(absolutePath, ext)}_${Date.now()}.mjs`);
|
|
1065
1300
|
let code = result.code;
|
|
1066
|
-
code = rewriteImports(code,
|
|
1067
|
-
|
|
1301
|
+
code = rewriteImports(code, path4.dirname(absolutePath));
|
|
1302
|
+
fs2.writeFileSync(tempFile, code);
|
|
1068
1303
|
try {
|
|
1069
1304
|
const module = await import(pathToFileURL(tempFile).href);
|
|
1070
1305
|
moduleCache.set(absolutePath, { module, mtime });
|
|
1306
|
+
if (useCache) {
|
|
1307
|
+
const cache2 = getCache();
|
|
1308
|
+
const sourceHash = __require("crypto").createHash("sha256").update(source).digest("hex").slice(0, 16);
|
|
1309
|
+
const cacheKey = `transform_${absolutePath}_${sourceHash}`;
|
|
1310
|
+
cache2.set(cacheKey, code, source);
|
|
1311
|
+
}
|
|
1071
1312
|
setImmediate(() => {
|
|
1072
1313
|
try {
|
|
1073
|
-
|
|
1314
|
+
fs2.unlinkSync(tempFile);
|
|
1074
1315
|
} catch {
|
|
1075
1316
|
}
|
|
1076
1317
|
});
|
|
1077
1318
|
return module;
|
|
1078
1319
|
} catch (error2) {
|
|
1079
1320
|
try {
|
|
1080
|
-
|
|
1321
|
+
fs2.unlinkSync(tempFile);
|
|
1081
1322
|
} catch {
|
|
1082
1323
|
}
|
|
1083
1324
|
throw error2;
|
|
@@ -1106,18 +1347,18 @@ function getLoader(ext) {
|
|
|
1106
1347
|
function rewriteImports(code, baseDir) {
|
|
1107
1348
|
const importRegex = /from\s+['"](\.[^'"]+)['"]/g;
|
|
1108
1349
|
return code.replace(importRegex, (match, importPath) => {
|
|
1109
|
-
let resolvedPath =
|
|
1350
|
+
let resolvedPath = path4.resolve(baseDir, importPath);
|
|
1110
1351
|
const extensions = [".tsx", ".ts", ".jsx", ".js", ".mjs", ""];
|
|
1111
1352
|
let found = false;
|
|
1112
1353
|
for (const ext of extensions) {
|
|
1113
1354
|
const tryPath = resolvedPath + ext;
|
|
1114
|
-
if (
|
|
1355
|
+
if (fs2.existsSync(tryPath)) {
|
|
1115
1356
|
resolvedPath = tryPath;
|
|
1116
1357
|
found = true;
|
|
1117
1358
|
break;
|
|
1118
1359
|
}
|
|
1119
|
-
const indexPath =
|
|
1120
|
-
if (
|
|
1360
|
+
const indexPath = path4.join(resolvedPath, `index${ext}`);
|
|
1361
|
+
if (fs2.existsSync(indexPath)) {
|
|
1121
1362
|
resolvedPath = indexPath;
|
|
1122
1363
|
found = true;
|
|
1123
1364
|
break;
|
|
@@ -1131,7 +1372,7 @@ function rewriteImports(code, baseDir) {
|
|
|
1131
1372
|
}
|
|
1132
1373
|
function clearModuleCache(filePath) {
|
|
1133
1374
|
if (filePath) {
|
|
1134
|
-
moduleCache.delete(
|
|
1375
|
+
moduleCache.delete(path4.resolve(filePath));
|
|
1135
1376
|
} else {
|
|
1136
1377
|
moduleCache.clear();
|
|
1137
1378
|
}
|
|
@@ -2520,11 +2761,214 @@ function generateExamplesPage() {
|
|
|
2520
2761
|
` + footer;
|
|
2521
2762
|
}
|
|
2522
2763
|
|
|
2764
|
+
// src/build/css-processor.ts
|
|
2765
|
+
init_esm_shims();
|
|
2766
|
+
import fs4 from "fs";
|
|
2767
|
+
import path6 from "path";
|
|
2768
|
+
|
|
2769
|
+
// src/build/tailwind-setup.ts
|
|
2770
|
+
init_esm_shims();
|
|
2771
|
+
import fs3 from "fs";
|
|
2772
|
+
import path5 from "path";
|
|
2773
|
+
import pc from "picocolors";
|
|
2774
|
+
function checkTailwindSetup(rootDir) {
|
|
2775
|
+
const possibleConfigs = [
|
|
2776
|
+
"tailwind.config.js",
|
|
2777
|
+
"tailwind.config.ts",
|
|
2778
|
+
"tailwind.config.mjs",
|
|
2779
|
+
"tailwind.config.cjs"
|
|
2780
|
+
];
|
|
2781
|
+
let configPath = null;
|
|
2782
|
+
for (const config of possibleConfigs) {
|
|
2783
|
+
const fullPath = path5.join(rootDir, config);
|
|
2784
|
+
if (fs3.existsSync(fullPath)) {
|
|
2785
|
+
configPath = fullPath;
|
|
2786
|
+
break;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
const globalsPath = path5.join(rootDir, "app", "globals.css");
|
|
2790
|
+
const hasGlobals = fs3.existsSync(globalsPath);
|
|
2791
|
+
return {
|
|
2792
|
+
hasTailwind: !!configPath,
|
|
2793
|
+
configPath,
|
|
2794
|
+
globalsPath: hasGlobals ? globalsPath : null,
|
|
2795
|
+
needsSetup: !configPath || !hasGlobals
|
|
2796
|
+
};
|
|
2797
|
+
}
|
|
2798
|
+
async function setupTailwind(rootDir, options = {}) {
|
|
2799
|
+
const { force = false, silent = false } = options;
|
|
2800
|
+
const config = checkTailwindSetup(rootDir);
|
|
2801
|
+
if (!force && !config.needsSetup) {
|
|
2802
|
+
return;
|
|
2803
|
+
}
|
|
2804
|
+
if (!silent) {
|
|
2805
|
+
console.log(pc.cyan("\n\u{1F3A8} Setting up Tailwind CSS..."));
|
|
2806
|
+
}
|
|
2807
|
+
if (!config.configPath || force) {
|
|
2808
|
+
const tailwindConfig = `/** @type {import('tailwindcss').Config} */
|
|
2809
|
+
export default {
|
|
2810
|
+
content: [
|
|
2811
|
+
'./app/**/*.{js,ts,jsx,tsx}',
|
|
2812
|
+
'./components/**/*.{js,ts,jsx,tsx}',
|
|
2813
|
+
],
|
|
2814
|
+
theme: {
|
|
2815
|
+
extend: {},
|
|
2816
|
+
},
|
|
2817
|
+
plugins: [],
|
|
2818
|
+
}
|
|
2819
|
+
`;
|
|
2820
|
+
const configPath = path5.join(rootDir, "tailwind.config.js");
|
|
2821
|
+
fs3.writeFileSync(configPath, tailwindConfig);
|
|
2822
|
+
if (!silent) {
|
|
2823
|
+
console.log(pc.green(" \u2713 Created tailwind.config.js"));
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
const postcssPath = path5.join(rootDir, "postcss.config.js");
|
|
2827
|
+
if (!fs3.existsSync(postcssPath) || force) {
|
|
2828
|
+
const postcssConfig = `export default {
|
|
2829
|
+
plugins: {
|
|
2830
|
+
tailwindcss: {},
|
|
2831
|
+
autoprefixer: {},
|
|
2832
|
+
},
|
|
2833
|
+
}
|
|
2834
|
+
`;
|
|
2835
|
+
fs3.writeFileSync(postcssPath, postcssConfig);
|
|
2836
|
+
if (!silent) {
|
|
2837
|
+
console.log(pc.green(" \u2713 Created postcss.config.js"));
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
const appDir = path5.join(rootDir, "app");
|
|
2841
|
+
if (!fs3.existsSync(appDir)) {
|
|
2842
|
+
fs3.mkdirSync(appDir, { recursive: true });
|
|
2843
|
+
}
|
|
2844
|
+
const globalsPath = path5.join(appDir, "globals.css");
|
|
2845
|
+
if (!fs3.existsSync(globalsPath) || force) {
|
|
2846
|
+
const globalsCss = `@tailwind base;
|
|
2847
|
+
@tailwind components;
|
|
2848
|
+
@tailwind utilities;
|
|
2849
|
+
`;
|
|
2850
|
+
fs3.writeFileSync(globalsPath, globalsCss);
|
|
2851
|
+
if (!silent) {
|
|
2852
|
+
console.log(pc.green(" \u2713 Created app/globals.css"));
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
const layoutPath = path5.join(appDir, "layout.tsx");
|
|
2856
|
+
if (!fs3.existsSync(layoutPath)) {
|
|
2857
|
+
const layoutContent = `import './globals.css'
|
|
2858
|
+
|
|
2859
|
+
export default function RootLayout({
|
|
2860
|
+
children,
|
|
2861
|
+
}: {
|
|
2862
|
+
children: React.ReactNode
|
|
2863
|
+
}) {
|
|
2864
|
+
return (
|
|
2865
|
+
<html lang="en">
|
|
2866
|
+
<body>{children}</body>
|
|
2867
|
+
</html>
|
|
2868
|
+
)
|
|
2869
|
+
}
|
|
2870
|
+
`;
|
|
2871
|
+
fs3.writeFileSync(layoutPath, layoutContent);
|
|
2872
|
+
if (!silent) {
|
|
2873
|
+
console.log(pc.green(" \u2713 Created app/layout.tsx"));
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
if (!silent) {
|
|
2877
|
+
console.log(pc.green("\n\u2728 Tailwind CSS ready!\n"));
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
function checkTailwindDeps(rootDir) {
|
|
2881
|
+
const packageJsonPath = path5.join(rootDir, "package.json");
|
|
2882
|
+
if (!fs3.existsSync(packageJsonPath)) {
|
|
2883
|
+
return {
|
|
2884
|
+
hasPackageJson: false,
|
|
2885
|
+
hasTailwind: false,
|
|
2886
|
+
hasPostCSS: false,
|
|
2887
|
+
hasAutoprefixer: false
|
|
2888
|
+
};
|
|
2889
|
+
}
|
|
2890
|
+
const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
|
|
2891
|
+
const allDeps = {
|
|
2892
|
+
...packageJson.dependencies,
|
|
2893
|
+
...packageJson.devDependencies
|
|
2894
|
+
};
|
|
2895
|
+
return {
|
|
2896
|
+
hasPackageJson: true,
|
|
2897
|
+
hasTailwind: !!allDeps["tailwindcss"],
|
|
2898
|
+
hasPostCSS: !!allDeps["postcss"],
|
|
2899
|
+
hasAutoprefixer: !!allDeps["autoprefixer"]
|
|
2900
|
+
};
|
|
2901
|
+
}
|
|
2902
|
+
function getTailwindInstallCommand(rootDir) {
|
|
2903
|
+
const deps = checkTailwindDeps(rootDir);
|
|
2904
|
+
if (!deps.hasPackageJson) {
|
|
2905
|
+
return null;
|
|
2906
|
+
}
|
|
2907
|
+
const missing = [];
|
|
2908
|
+
if (!deps.hasTailwind) missing.push("tailwindcss");
|
|
2909
|
+
if (!deps.hasPostCSS) missing.push("postcss");
|
|
2910
|
+
if (!deps.hasAutoprefixer) missing.push("autoprefixer");
|
|
2911
|
+
if (missing.length === 0) {
|
|
2912
|
+
return null;
|
|
2913
|
+
}
|
|
2914
|
+
const hasYarnLock = fs3.existsSync(path5.join(rootDir, "yarn.lock"));
|
|
2915
|
+
const hasPnpmLock = fs3.existsSync(path5.join(rootDir, "pnpm-lock.yaml"));
|
|
2916
|
+
const hasBunLock = fs3.existsSync(path5.join(rootDir, "bun.lockb"));
|
|
2917
|
+
let pm = "npm install -D";
|
|
2918
|
+
if (hasBunLock) pm = "bun add -d";
|
|
2919
|
+
else if (hasPnpmLock) pm = "pnpm add -D";
|
|
2920
|
+
else if (hasYarnLock) pm = "yarn add -D";
|
|
2921
|
+
return `${pm} ${missing.join(" ")}`;
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
// src/build/css-processor.ts
|
|
2925
|
+
async function processCSS(filePath, rootDir = process.cwd()) {
|
|
2926
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
2927
|
+
const tailwindConfig = checkTailwindSetup(rootDir);
|
|
2928
|
+
if (!tailwindConfig.hasTailwind) {
|
|
2929
|
+
return { code: content };
|
|
2930
|
+
}
|
|
2931
|
+
try {
|
|
2932
|
+
const postcss = await import("postcss").then((m) => m.default);
|
|
2933
|
+
const tailwindcss = await import("tailwindcss").then((m) => m.default);
|
|
2934
|
+
const autoprefixer = await import("autoprefixer").then((m) => m.default);
|
|
2935
|
+
const configPath = tailwindConfig.configPath || path6.join(rootDir, "tailwind.config.js");
|
|
2936
|
+
let tailwindConfigModule = {};
|
|
2937
|
+
if (fs4.existsSync(configPath)) {
|
|
2938
|
+
const configUrl = new URL(`file://${configPath}`);
|
|
2939
|
+
tailwindConfigModule = await import(configUrl.href).then((m) => m.default || m);
|
|
2940
|
+
}
|
|
2941
|
+
const result = await postcss([
|
|
2942
|
+
tailwindcss(tailwindConfigModule),
|
|
2943
|
+
autoprefixer()
|
|
2944
|
+
]).process(content, {
|
|
2945
|
+
from: filePath,
|
|
2946
|
+
to: filePath,
|
|
2947
|
+
map: { inline: false }
|
|
2948
|
+
});
|
|
2949
|
+
return {
|
|
2950
|
+
code: result.css,
|
|
2951
|
+
map: result.map?.toString()
|
|
2952
|
+
};
|
|
2953
|
+
} catch (error2) {
|
|
2954
|
+
console.warn("CSS processing failed, serving raw CSS:", error2);
|
|
2955
|
+
return { code: content };
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
function needsCSSProcessing(filePath, rootDir) {
|
|
2959
|
+
const config = checkTailwindSetup(rootDir);
|
|
2960
|
+
if (!config.hasTailwind) {
|
|
2961
|
+
return false;
|
|
2962
|
+
}
|
|
2963
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
2964
|
+
return content.includes("@tailwind") || content.includes("@apply");
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2523
2967
|
// src/server/dev-server.ts
|
|
2524
2968
|
async function createDevServer(options) {
|
|
2525
2969
|
const { port, host, open } = options;
|
|
2526
2970
|
const rootDir = process.cwd();
|
|
2527
|
-
const publicDir =
|
|
2971
|
+
const publicDir = path7.join(rootDir, "public");
|
|
2528
2972
|
let routes = [];
|
|
2529
2973
|
let server = null;
|
|
2530
2974
|
let wss = null;
|
|
@@ -2532,9 +2976,20 @@ async function createDevServer(options) {
|
|
|
2532
2976
|
async function refreshRoutes() {
|
|
2533
2977
|
try {
|
|
2534
2978
|
routes = await scanRoutes(rootDir);
|
|
2535
|
-
console.log(
|
|
2979
|
+
console.log(pc2.dim(` \u{1F4C1} Found ${routes.length} routes`));
|
|
2980
|
+
const tailwindConfig = checkTailwindSetup(rootDir);
|
|
2981
|
+
if (tailwindConfig.needsSetup) {
|
|
2982
|
+
console.log(pc2.yellow(" \u26A0\uFE0F Tailwind not configured"));
|
|
2983
|
+
const installCmd = getTailwindInstallCommand(rootDir);
|
|
2984
|
+
if (installCmd) {
|
|
2985
|
+
console.log(pc2.dim(` \u{1F4A1} Run: ${installCmd}`));
|
|
2986
|
+
console.log(pc2.dim(` \u{1F4A1} Then: npx float dev`));
|
|
2987
|
+
} else {
|
|
2988
|
+
await setupTailwind(rootDir);
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2536
2991
|
} catch (error2) {
|
|
2537
|
-
console.error(
|
|
2992
|
+
console.error(pc2.red("Failed to scan routes:"), error2);
|
|
2538
2993
|
}
|
|
2539
2994
|
}
|
|
2540
2995
|
function notifyClients(type, data) {
|
|
@@ -2595,16 +3050,46 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2595
3050
|
async function handleRequest(req, res) {
|
|
2596
3051
|
const url = new URL(req.url || "/", `http://${host}:${port}`);
|
|
2597
3052
|
const pathname = url.pathname;
|
|
2598
|
-
console.log(
|
|
3053
|
+
console.log(pc2.dim(` ${req.method} ${pathname}`));
|
|
2599
3054
|
try {
|
|
2600
|
-
const publicFilePath =
|
|
2601
|
-
if (
|
|
2602
|
-
const content =
|
|
3055
|
+
const publicFilePath = path7.join(publicDir, pathname);
|
|
3056
|
+
if (fs5.existsSync(publicFilePath) && fs5.statSync(publicFilePath).isFile()) {
|
|
3057
|
+
const content = fs5.readFileSync(publicFilePath);
|
|
2603
3058
|
const contentType = mime.lookup(publicFilePath) || "application/octet-stream";
|
|
2604
3059
|
res.writeHead(200, { "Content-Type": contentType });
|
|
2605
3060
|
res.end(content);
|
|
2606
3061
|
return;
|
|
2607
3062
|
}
|
|
3063
|
+
if (pathname.endsWith(".css")) {
|
|
3064
|
+
const cssPath = path7.join(rootDir, "app", pathname.replace(/^\//, ""));
|
|
3065
|
+
if (fs5.existsSync(cssPath)) {
|
|
3066
|
+
try {
|
|
3067
|
+
const needsProcessing = needsCSSProcessing(cssPath, rootDir);
|
|
3068
|
+
if (needsProcessing) {
|
|
3069
|
+
const result = await processCSS(cssPath, rootDir);
|
|
3070
|
+
res.writeHead(200, {
|
|
3071
|
+
"Content-Type": "text/css",
|
|
3072
|
+
"Cache-Control": "no-cache"
|
|
3073
|
+
});
|
|
3074
|
+
res.end(result.code);
|
|
3075
|
+
} else {
|
|
3076
|
+
const content = fs5.readFileSync(cssPath, "utf-8");
|
|
3077
|
+
res.writeHead(200, {
|
|
3078
|
+
"Content-Type": "text/css",
|
|
3079
|
+
"Cache-Control": "no-cache"
|
|
3080
|
+
});
|
|
3081
|
+
res.end(content);
|
|
3082
|
+
}
|
|
3083
|
+
return;
|
|
3084
|
+
} catch (error2) {
|
|
3085
|
+
console.error(pc2.red("CSS processing error:"), error2);
|
|
3086
|
+
const content = fs5.readFileSync(cssPath, "utf-8");
|
|
3087
|
+
res.writeHead(200, { "Content-Type": "text/css" });
|
|
3088
|
+
res.end(content);
|
|
3089
|
+
return;
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
2608
3093
|
if (pathname.startsWith("/_float/")) {
|
|
2609
3094
|
res.writeHead(200, { "Content-Type": "application/javascript" });
|
|
2610
3095
|
res.end("// Float.js internal asset");
|
|
@@ -2669,7 +3154,7 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2669
3154
|
});
|
|
2670
3155
|
res.end(html);
|
|
2671
3156
|
} catch (error2) {
|
|
2672
|
-
console.error(
|
|
3157
|
+
console.error(pc2.red("Request error:"), error2);
|
|
2673
3158
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
2674
3159
|
res.end(createErrorPage(error2));
|
|
2675
3160
|
}
|
|
@@ -2698,7 +3183,7 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2698
3183
|
const responseBody = await response.text();
|
|
2699
3184
|
res.end(responseBody);
|
|
2700
3185
|
} catch (error2) {
|
|
2701
|
-
console.error(
|
|
3186
|
+
console.error(pc2.red("API route error:"), error2);
|
|
2702
3187
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
2703
3188
|
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
2704
3189
|
}
|
|
@@ -2713,9 +3198,9 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2713
3198
|
});
|
|
2714
3199
|
const watcher = chokidar.watch(
|
|
2715
3200
|
[
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
3201
|
+
path7.join(rootDir, "app/**/*.{ts,tsx,js,jsx}"),
|
|
3202
|
+
path7.join(rootDir, "components/**/*.{ts,tsx,js,jsx}"),
|
|
3203
|
+
path7.join(rootDir, "lib/**/*.{ts,tsx,js,jsx}")
|
|
2719
3204
|
],
|
|
2720
3205
|
{
|
|
2721
3206
|
ignored: /node_modules/,
|
|
@@ -2723,8 +3208,8 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2723
3208
|
}
|
|
2724
3209
|
);
|
|
2725
3210
|
watcher.on("change", async (filePath) => {
|
|
2726
|
-
console.log(
|
|
2727
|
-
\u26A1 File changed: ${
|
|
3211
|
+
console.log(pc2.yellow(`
|
|
3212
|
+
\u26A1 File changed: ${path7.relative(rootDir, filePath)}`));
|
|
2728
3213
|
if (filePath.includes("/app/")) {
|
|
2729
3214
|
await refreshRoutes();
|
|
2730
3215
|
}
|
|
@@ -2732,31 +3217,31 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2732
3217
|
});
|
|
2733
3218
|
watcher.on("add", async (filePath) => {
|
|
2734
3219
|
if (filePath.includes("/app/")) {
|
|
2735
|
-
console.log(
|
|
2736
|
-
\u2795 File added: ${
|
|
3220
|
+
console.log(pc2.green(`
|
|
3221
|
+
\u2795 File added: ${path7.relative(rootDir, filePath)}`));
|
|
2737
3222
|
await refreshRoutes();
|
|
2738
3223
|
notifyClients("reload");
|
|
2739
3224
|
}
|
|
2740
3225
|
});
|
|
2741
3226
|
watcher.on("unlink", async (filePath) => {
|
|
2742
3227
|
if (filePath.includes("/app/")) {
|
|
2743
|
-
console.log(
|
|
2744
|
-
\u2796 File removed: ${
|
|
3228
|
+
console.log(pc2.red(`
|
|
3229
|
+
\u2796 File removed: ${path7.relative(rootDir, filePath)}`));
|
|
2745
3230
|
await refreshRoutes();
|
|
2746
3231
|
notifyClients("reload");
|
|
2747
3232
|
}
|
|
2748
3233
|
});
|
|
2749
3234
|
return new Promise((resolve, reject) => {
|
|
2750
3235
|
server.listen(port, host, () => {
|
|
2751
|
-
console.log(
|
|
2752
|
-
console.log(
|
|
3236
|
+
console.log(pc2.green(` \u2705 Server running at ${pc2.cyan(`http://${host}:${port}`)}`));
|
|
3237
|
+
console.log(pc2.dim(` \u26A1 HMR enabled on ws://${host}:${port + 1}
|
|
2753
3238
|
`));
|
|
2754
|
-
console.log(
|
|
3239
|
+
console.log(pc2.bold(" Routes:"));
|
|
2755
3240
|
routes.forEach((route) => {
|
|
2756
3241
|
if (route.type === "page") {
|
|
2757
|
-
console.log(
|
|
3242
|
+
console.log(pc2.dim(` ${pc2.green("\u25CF")} ${route.path}`));
|
|
2758
3243
|
} else if (route.type === "api") {
|
|
2759
|
-
console.log(
|
|
3244
|
+
console.log(pc2.dim(` ${pc2.blue("\u25C6")} ${route.path} (API)`));
|
|
2760
3245
|
}
|
|
2761
3246
|
});
|
|
2762
3247
|
console.log("");
|
|
@@ -2876,45 +3361,45 @@ function escapeHtml2(text) {
|
|
|
2876
3361
|
// src/server/prod-server.ts
|
|
2877
3362
|
init_esm_shims();
|
|
2878
3363
|
import http2 from "http";
|
|
2879
|
-
import
|
|
2880
|
-
import
|
|
2881
|
-
import
|
|
3364
|
+
import fs6 from "fs";
|
|
3365
|
+
import path8 from "path";
|
|
3366
|
+
import pc3 from "picocolors";
|
|
2882
3367
|
import mime2 from "mime-types";
|
|
2883
3368
|
var cachedRoutes = [];
|
|
2884
3369
|
var pageCache = /* @__PURE__ */ new Map();
|
|
2885
3370
|
async function startProductionServer(options) {
|
|
2886
3371
|
const { port, host } = options;
|
|
2887
3372
|
const rootDir = process.cwd();
|
|
2888
|
-
const distDir =
|
|
2889
|
-
const publicDir =
|
|
2890
|
-
const manifestPath =
|
|
2891
|
-
if (
|
|
2892
|
-
const manifest = JSON.parse(
|
|
3373
|
+
const distDir = path8.join(rootDir, ".float");
|
|
3374
|
+
const publicDir = path8.join(rootDir, "public");
|
|
3375
|
+
const manifestPath = path8.join(distDir, "routes-manifest.json");
|
|
3376
|
+
if (fs6.existsSync(manifestPath)) {
|
|
3377
|
+
const manifest = JSON.parse(fs6.readFileSync(manifestPath, "utf-8"));
|
|
2893
3378
|
cachedRoutes = manifest.routes;
|
|
2894
|
-
console.log(
|
|
3379
|
+
console.log(pc3.dim(` \u{1F4E6} Loaded ${cachedRoutes.length} routes from manifest`));
|
|
2895
3380
|
} else {
|
|
2896
|
-
console.error(
|
|
3381
|
+
console.error(pc3.red(" \u274C No build manifest found. Run `float build` first."));
|
|
2897
3382
|
process.exit(1);
|
|
2898
3383
|
}
|
|
2899
|
-
const pagesDir =
|
|
2900
|
-
if (
|
|
2901
|
-
const prerenderedFiles =
|
|
3384
|
+
const pagesDir = path8.join(distDir, "pages");
|
|
3385
|
+
if (fs6.existsSync(pagesDir)) {
|
|
3386
|
+
const prerenderedFiles = fs6.readdirSync(pagesDir, { recursive: true });
|
|
2902
3387
|
for (const file of prerenderedFiles) {
|
|
2903
3388
|
if (file.endsWith(".html")) {
|
|
2904
3389
|
const routePath = "/" + file.replace(/\.html$/, "").replace(/index$/, "");
|
|
2905
|
-
const content =
|
|
3390
|
+
const content = fs6.readFileSync(path8.join(pagesDir, file), "utf-8");
|
|
2906
3391
|
pageCache.set(routePath, content);
|
|
2907
3392
|
}
|
|
2908
3393
|
}
|
|
2909
|
-
console.log(
|
|
3394
|
+
console.log(pc3.dim(` \u{1F4C4} Loaded ${pageCache.size} pre-rendered pages`));
|
|
2910
3395
|
}
|
|
2911
3396
|
const server = http2.createServer(async (req, res) => {
|
|
2912
3397
|
const url = new URL(req.url || "/", `http://${host}:${port}`);
|
|
2913
3398
|
const pathname = url.pathname;
|
|
2914
3399
|
try {
|
|
2915
|
-
const staticPath =
|
|
2916
|
-
if (
|
|
2917
|
-
const content =
|
|
3400
|
+
const staticPath = path8.join(distDir, "static", pathname);
|
|
3401
|
+
if (fs6.existsSync(staticPath) && fs6.statSync(staticPath).isFile()) {
|
|
3402
|
+
const content = fs6.readFileSync(staticPath);
|
|
2918
3403
|
const contentType = mime2.lookup(staticPath) || "application/octet-stream";
|
|
2919
3404
|
res.writeHead(200, {
|
|
2920
3405
|
"Content-Type": contentType,
|
|
@@ -2923,9 +3408,9 @@ async function startProductionServer(options) {
|
|
|
2923
3408
|
res.end(content);
|
|
2924
3409
|
return;
|
|
2925
3410
|
}
|
|
2926
|
-
const publicFilePath =
|
|
2927
|
-
if (
|
|
2928
|
-
const content =
|
|
3411
|
+
const publicFilePath = path8.join(publicDir, pathname);
|
|
3412
|
+
if (fs6.existsSync(publicFilePath) && fs6.statSync(publicFilePath).isFile()) {
|
|
3413
|
+
const content = fs6.readFileSync(publicFilePath);
|
|
2929
3414
|
const contentType = mime2.lookup(publicFilePath) || "application/octet-stream";
|
|
2930
3415
|
res.writeHead(200, { "Content-Type": contentType });
|
|
2931
3416
|
res.end(content);
|
|
@@ -2958,13 +3443,13 @@ async function startProductionServer(options) {
|
|
|
2958
3443
|
});
|
|
2959
3444
|
res.end(html);
|
|
2960
3445
|
} catch (error2) {
|
|
2961
|
-
console.error(
|
|
3446
|
+
console.error(pc3.red("Request error:"), error2);
|
|
2962
3447
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
2963
3448
|
res.end("<h1>500 - Internal Server Error</h1>");
|
|
2964
3449
|
}
|
|
2965
3450
|
});
|
|
2966
3451
|
server.listen(port, host, () => {
|
|
2967
|
-
console.log(
|
|
3452
|
+
console.log(pc3.green(` \u2705 Production server running at ${pc3.cyan(`http://${host}:${port}`)}
|
|
2968
3453
|
`));
|
|
2969
3454
|
});
|
|
2970
3455
|
}
|
|
@@ -2972,9 +3457,9 @@ async function startProductionServer(options) {
|
|
|
2972
3457
|
// src/build/index.ts
|
|
2973
3458
|
init_esm_shims();
|
|
2974
3459
|
import * as esbuild2 from "esbuild";
|
|
2975
|
-
import
|
|
2976
|
-
import
|
|
2977
|
-
import
|
|
3460
|
+
import fs7 from "fs";
|
|
3461
|
+
import path9 from "path";
|
|
3462
|
+
import pc4 from "picocolors";
|
|
2978
3463
|
var DEFAULT_BUILD_OPTIONS = {
|
|
2979
3464
|
analyze: false,
|
|
2980
3465
|
minify: true,
|
|
@@ -2984,27 +3469,27 @@ async function build2(options = {}) {
|
|
|
2984
3469
|
const opts = { ...DEFAULT_BUILD_OPTIONS, ...options };
|
|
2985
3470
|
const startTime = Date.now();
|
|
2986
3471
|
const rootDir = process.cwd();
|
|
2987
|
-
const outputDir =
|
|
2988
|
-
if (
|
|
2989
|
-
|
|
2990
|
-
}
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
console.log(
|
|
3472
|
+
const outputDir = path9.join(rootDir, ".float");
|
|
3473
|
+
if (fs7.existsSync(outputDir)) {
|
|
3474
|
+
fs7.rmSync(outputDir, { recursive: true });
|
|
3475
|
+
}
|
|
3476
|
+
fs7.mkdirSync(outputDir, { recursive: true });
|
|
3477
|
+
fs7.mkdirSync(path9.join(outputDir, "pages"), { recursive: true });
|
|
3478
|
+
fs7.mkdirSync(path9.join(outputDir, "static"), { recursive: true });
|
|
3479
|
+
fs7.mkdirSync(path9.join(outputDir, "server"), { recursive: true });
|
|
3480
|
+
console.log(pc4.dim(" Scanning routes..."));
|
|
2996
3481
|
const routes = await scanRoutes(rootDir);
|
|
2997
3482
|
const pageRoutes = routes.filter((r) => r.type === "page" && !r.params.length);
|
|
2998
3483
|
const dynamicRoutes = routes.filter((r) => r.type === "page" && r.params.length > 0);
|
|
2999
3484
|
const apiRoutes = routes.filter((r) => r.type === "api");
|
|
3000
|
-
console.log(
|
|
3001
|
-
console.log(
|
|
3485
|
+
console.log(pc4.dim(` Found ${pageRoutes.length} static pages, ${dynamicRoutes.length} dynamic routes, ${apiRoutes.length} API routes`));
|
|
3486
|
+
console.log(pc4.dim(" Building client bundle..."));
|
|
3002
3487
|
const clientEntryPoints = routes.filter((r) => r.type === "page").map((r) => r.absolutePath);
|
|
3003
3488
|
if (clientEntryPoints.length > 0) {
|
|
3004
3489
|
await esbuild2.build({
|
|
3005
3490
|
entryPoints: clientEntryPoints,
|
|
3006
3491
|
bundle: true,
|
|
3007
|
-
outdir:
|
|
3492
|
+
outdir: path9.join(outputDir, "static", "_float"),
|
|
3008
3493
|
format: "esm",
|
|
3009
3494
|
splitting: true,
|
|
3010
3495
|
minify: opts.minify,
|
|
@@ -3026,13 +3511,13 @@ async function build2(options = {}) {
|
|
|
3026
3511
|
metafile: opts.analyze
|
|
3027
3512
|
});
|
|
3028
3513
|
}
|
|
3029
|
-
console.log(
|
|
3514
|
+
console.log(pc4.dim(" Building server bundle..."));
|
|
3030
3515
|
const serverEntryPoints = routes.map((r) => r.absolutePath);
|
|
3031
3516
|
if (serverEntryPoints.length > 0) {
|
|
3032
3517
|
await esbuild2.build({
|
|
3033
3518
|
entryPoints: serverEntryPoints,
|
|
3034
3519
|
bundle: true,
|
|
3035
|
-
outdir:
|
|
3520
|
+
outdir: path9.join(outputDir, "server"),
|
|
3036
3521
|
format: "esm",
|
|
3037
3522
|
platform: "node",
|
|
3038
3523
|
target: ["node18"],
|
|
@@ -3049,26 +3534,26 @@ async function build2(options = {}) {
|
|
|
3049
3534
|
external: ["react", "react-dom", "@float/core"]
|
|
3050
3535
|
});
|
|
3051
3536
|
}
|
|
3052
|
-
console.log(
|
|
3537
|
+
console.log(pc4.dim(" Pre-rendering static pages..."));
|
|
3053
3538
|
const prerenderedPages = [];
|
|
3054
3539
|
for (const route of pageRoutes) {
|
|
3055
3540
|
try {
|
|
3056
3541
|
const html = await renderPage(route, {}, { isDev: false });
|
|
3057
|
-
const outputPath = route.path === "/" ?
|
|
3058
|
-
|
|
3059
|
-
|
|
3542
|
+
const outputPath = route.path === "/" ? path9.join(outputDir, "pages", "index.html") : path9.join(outputDir, "pages", route.path, "index.html");
|
|
3543
|
+
fs7.mkdirSync(path9.dirname(outputPath), { recursive: true });
|
|
3544
|
+
fs7.writeFileSync(outputPath, html);
|
|
3060
3545
|
prerenderedPages.push(route.path);
|
|
3061
|
-
console.log(
|
|
3546
|
+
console.log(pc4.dim(` \u2713 ${route.path}`));
|
|
3062
3547
|
} catch (error2) {
|
|
3063
|
-
console.log(
|
|
3548
|
+
console.log(pc4.yellow(` \u26A0 ${route.path} (will render at runtime)`));
|
|
3064
3549
|
}
|
|
3065
3550
|
}
|
|
3066
|
-
console.log(
|
|
3551
|
+
console.log(pc4.dim(" Building API routes..."));
|
|
3067
3552
|
for (const route of apiRoutes) {
|
|
3068
3553
|
await esbuild2.build({
|
|
3069
3554
|
entryPoints: [route.absolutePath],
|
|
3070
3555
|
bundle: true,
|
|
3071
|
-
outfile:
|
|
3556
|
+
outfile: path9.join(outputDir, "server", "api", `${route.path.replace(/\//g, "_")}.js`),
|
|
3072
3557
|
format: "esm",
|
|
3073
3558
|
platform: "neutral",
|
|
3074
3559
|
// Edge compatible
|
|
@@ -3076,10 +3561,10 @@ async function build2(options = {}) {
|
|
|
3076
3561
|
minify: true
|
|
3077
3562
|
});
|
|
3078
3563
|
}
|
|
3079
|
-
const publicDir =
|
|
3080
|
-
if (
|
|
3081
|
-
console.log(
|
|
3082
|
-
copyDir(publicDir,
|
|
3564
|
+
const publicDir = path9.join(rootDir, "public");
|
|
3565
|
+
if (fs7.existsSync(publicDir)) {
|
|
3566
|
+
console.log(pc4.dim(" Copying public assets..."));
|
|
3567
|
+
copyDir(publicDir, path9.join(outputDir, "static"));
|
|
3083
3568
|
}
|
|
3084
3569
|
const manifest = {
|
|
3085
3570
|
version: 1,
|
|
@@ -3088,19 +3573,19 @@ async function build2(options = {}) {
|
|
|
3088
3573
|
path: r.path,
|
|
3089
3574
|
type: r.type,
|
|
3090
3575
|
filePath: r.filePath,
|
|
3091
|
-
absolutePath:
|
|
3576
|
+
absolutePath: path9.relative(rootDir, r.absolutePath),
|
|
3092
3577
|
params: r.params,
|
|
3093
3578
|
isCatchAll: r.isCatchAll,
|
|
3094
3579
|
isOptionalCatchAll: r.isOptionalCatchAll,
|
|
3095
|
-
layouts: r.layouts.map((l) =>
|
|
3580
|
+
layouts: r.layouts.map((l) => path9.relative(rootDir, l)),
|
|
3096
3581
|
prerendered: prerenderedPages.includes(r.path)
|
|
3097
3582
|
})),
|
|
3098
3583
|
staticPages: prerenderedPages,
|
|
3099
3584
|
dynamicRoutes: dynamicRoutes.map((r) => r.path),
|
|
3100
3585
|
apiRoutes: apiRoutes.map((r) => r.path)
|
|
3101
3586
|
};
|
|
3102
|
-
|
|
3103
|
-
|
|
3587
|
+
fs7.writeFileSync(
|
|
3588
|
+
path9.join(outputDir, "routes-manifest.json"),
|
|
3104
3589
|
JSON.stringify(manifest, null, 2)
|
|
3105
3590
|
);
|
|
3106
3591
|
const duration = Date.now() - startTime;
|
|
@@ -3110,16 +3595,16 @@ async function build2(options = {}) {
|
|
|
3110
3595
|
routes: routes.length,
|
|
3111
3596
|
pages: prerenderedPages.length
|
|
3112
3597
|
};
|
|
3113
|
-
|
|
3114
|
-
|
|
3598
|
+
fs7.writeFileSync(
|
|
3599
|
+
path9.join(outputDir, "build-info.json"),
|
|
3115
3600
|
JSON.stringify(buildInfo, null, 2)
|
|
3116
3601
|
);
|
|
3117
3602
|
console.log("");
|
|
3118
|
-
console.log(
|
|
3119
|
-
console.log(
|
|
3120
|
-
console.log(
|
|
3121
|
-
console.log(
|
|
3122
|
-
console.log(
|
|
3603
|
+
console.log(pc4.bold(" Build Summary:"));
|
|
3604
|
+
console.log(pc4.dim(` Static Pages: ${prerenderedPages.length}`));
|
|
3605
|
+
console.log(pc4.dim(` Dynamic Routes: ${dynamicRoutes.length}`));
|
|
3606
|
+
console.log(pc4.dim(` API Routes: ${apiRoutes.length}`));
|
|
3607
|
+
console.log(pc4.dim(` Output: .float/`));
|
|
3123
3608
|
return {
|
|
3124
3609
|
routes,
|
|
3125
3610
|
duration,
|
|
@@ -3129,15 +3614,15 @@ async function build2(options = {}) {
|
|
|
3129
3614
|
};
|
|
3130
3615
|
}
|
|
3131
3616
|
function copyDir(src, dest) {
|
|
3132
|
-
|
|
3133
|
-
const entries =
|
|
3617
|
+
fs7.mkdirSync(dest, { recursive: true });
|
|
3618
|
+
const entries = fs7.readdirSync(src, { withFileTypes: true });
|
|
3134
3619
|
for (const entry of entries) {
|
|
3135
|
-
const srcPath =
|
|
3136
|
-
const destPath =
|
|
3620
|
+
const srcPath = path9.join(src, entry.name);
|
|
3621
|
+
const destPath = path9.join(dest, entry.name);
|
|
3137
3622
|
if (entry.isDirectory()) {
|
|
3138
3623
|
copyDir(srcPath, destPath);
|
|
3139
3624
|
} else {
|
|
3140
|
-
|
|
3625
|
+
fs7.copyFileSync(srcPath, destPath);
|
|
3141
3626
|
}
|
|
3142
3627
|
}
|
|
3143
3628
|
}
|
|
@@ -5590,10 +6075,10 @@ function matcherToRegex(pattern) {
|
|
|
5590
6075
|
let regex = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "(?<$1>[^/]+)").replace(/\\\*/g, ".*").replace(/\\\(([^)]+)\\\)/g, "($1)?");
|
|
5591
6076
|
return new RegExp(`^${regex}$`);
|
|
5592
6077
|
}
|
|
5593
|
-
function matchesPath(
|
|
6078
|
+
function matchesPath(path10, matcher) {
|
|
5594
6079
|
if (!matcher) return true;
|
|
5595
6080
|
const patterns = Array.isArray(matcher) ? matcher : [matcher];
|
|
5596
|
-
return patterns.some((pattern) => matcherToRegex(pattern).test(
|
|
6081
|
+
return patterns.some((pattern) => matcherToRegex(pattern).test(path10));
|
|
5597
6082
|
}
|
|
5598
6083
|
function parseCookies(cookieHeader) {
|
|
5599
6084
|
const cookies = /* @__PURE__ */ new Map();
|
|
@@ -5839,14 +6324,14 @@ var middlewareHelpers = {
|
|
|
5839
6324
|
*/
|
|
5840
6325
|
trailingSlash(add = false) {
|
|
5841
6326
|
return (req) => {
|
|
5842
|
-
const
|
|
5843
|
-
if (
|
|
5844
|
-
const hasSlash =
|
|
6327
|
+
const path10 = req.nextUrl.pathname;
|
|
6328
|
+
if (path10 === "/") return NextResponse.next();
|
|
6329
|
+
const hasSlash = path10.endsWith("/");
|
|
5845
6330
|
if (add && !hasSlash) {
|
|
5846
|
-
return NextResponse.redirect(new URL(
|
|
6331
|
+
return NextResponse.redirect(new URL(path10 + "/", req.request.url));
|
|
5847
6332
|
}
|
|
5848
6333
|
if (!add && hasSlash) {
|
|
5849
|
-
return NextResponse.redirect(new URL(
|
|
6334
|
+
return NextResponse.redirect(new URL(path10.slice(0, -1), req.request.url));
|
|
5850
6335
|
}
|
|
5851
6336
|
return NextResponse.next();
|
|
5852
6337
|
};
|
|
@@ -5884,8 +6369,8 @@ var SSGEngine = class {
|
|
|
5884
6369
|
mkdirSync2(this.diskCachePath, { recursive: true });
|
|
5885
6370
|
}
|
|
5886
6371
|
}
|
|
5887
|
-
getCacheKey(
|
|
5888
|
-
const normalized =
|
|
6372
|
+
getCacheKey(path10, locale) {
|
|
6373
|
+
const normalized = path10.replace(/^\/+|\/+$/g, "") || "index";
|
|
5889
6374
|
const key = locale ? `${locale}/${normalized}` : normalized;
|
|
5890
6375
|
return key.replace(/\//g, "__");
|
|
5891
6376
|
}
|
|
@@ -5898,9 +6383,9 @@ var SSGEngine = class {
|
|
|
5898
6383
|
/**
|
|
5899
6384
|
* Generate static page and cache it
|
|
5900
6385
|
*/
|
|
5901
|
-
async generatePage(
|
|
6386
|
+
async generatePage(path10, getStaticProps, render, params = {}, locale) {
|
|
5902
6387
|
const startTime = Date.now();
|
|
5903
|
-
const cacheKey = this.getCacheKey(
|
|
6388
|
+
const cacheKey = this.getCacheKey(path10, locale);
|
|
5904
6389
|
try {
|
|
5905
6390
|
const propsResult = await getStaticProps({
|
|
5906
6391
|
params,
|
|
@@ -5909,7 +6394,7 @@ var SSGEngine = class {
|
|
|
5909
6394
|
});
|
|
5910
6395
|
if (propsResult.redirect) {
|
|
5911
6396
|
return {
|
|
5912
|
-
path:
|
|
6397
|
+
path: path10,
|
|
5913
6398
|
success: true,
|
|
5914
6399
|
duration: Date.now() - startTime,
|
|
5915
6400
|
size: 0
|
|
@@ -5917,7 +6402,7 @@ var SSGEngine = class {
|
|
|
5917
6402
|
}
|
|
5918
6403
|
if (propsResult.notFound) {
|
|
5919
6404
|
return {
|
|
5920
|
-
path:
|
|
6405
|
+
path: path10,
|
|
5921
6406
|
success: false,
|
|
5922
6407
|
duration: Date.now() - startTime,
|
|
5923
6408
|
size: 0,
|
|
@@ -5941,7 +6426,7 @@ var SSGEngine = class {
|
|
|
5941
6426
|
this.setMemoryCache(cacheKey, cachedPage);
|
|
5942
6427
|
this.writeToDisk(cacheKey, cachedPage);
|
|
5943
6428
|
return {
|
|
5944
|
-
path:
|
|
6429
|
+
path: path10,
|
|
5945
6430
|
success: true,
|
|
5946
6431
|
duration: Date.now() - startTime,
|
|
5947
6432
|
size: html.length,
|
|
@@ -5949,7 +6434,7 @@ var SSGEngine = class {
|
|
|
5949
6434
|
};
|
|
5950
6435
|
} catch (error2) {
|
|
5951
6436
|
return {
|
|
5952
|
-
path:
|
|
6437
|
+
path: path10,
|
|
5953
6438
|
success: false,
|
|
5954
6439
|
duration: Date.now() - startTime,
|
|
5955
6440
|
size: 0,
|
|
@@ -5960,8 +6445,8 @@ var SSGEngine = class {
|
|
|
5960
6445
|
/**
|
|
5961
6446
|
* Get cached page or generate on-demand
|
|
5962
6447
|
*/
|
|
5963
|
-
async getPage(
|
|
5964
|
-
const cacheKey = this.getCacheKey(
|
|
6448
|
+
async getPage(path10, getStaticProps, render, params = {}, locale, fallback = false) {
|
|
6449
|
+
const cacheKey = this.getCacheKey(path10, locale);
|
|
5965
6450
|
let cached = this.memoryCache.get(cacheKey);
|
|
5966
6451
|
if (!cached) {
|
|
5967
6452
|
cached = this.readFromDisk(cacheKey) || void 0;
|
|
@@ -5972,7 +6457,7 @@ var SSGEngine = class {
|
|
|
5972
6457
|
if (cached) {
|
|
5973
6458
|
const isStale = cached.revalidateAfter !== null && Date.now() > cached.revalidateAfter;
|
|
5974
6459
|
if (isStale && this.config.staleWhileRevalidate) {
|
|
5975
|
-
this.triggerRevalidation(
|
|
6460
|
+
this.triggerRevalidation(path10, getStaticProps, render, params, locale);
|
|
5976
6461
|
}
|
|
5977
6462
|
return { cached, stale: isStale };
|
|
5978
6463
|
}
|
|
@@ -5980,7 +6465,7 @@ var SSGEngine = class {
|
|
|
5980
6465
|
return null;
|
|
5981
6466
|
}
|
|
5982
6467
|
if (fallback === "blocking") {
|
|
5983
|
-
const result = await this.generatePage(
|
|
6468
|
+
const result = await this.generatePage(path10, getStaticProps, render, params, locale);
|
|
5984
6469
|
if (result.success) {
|
|
5985
6470
|
const newCached = this.memoryCache.get(cacheKey);
|
|
5986
6471
|
return newCached ? { cached: newCached, stale: false } : null;
|
|
@@ -5991,14 +6476,14 @@ var SSGEngine = class {
|
|
|
5991
6476
|
/**
|
|
5992
6477
|
* Trigger ISR revalidation in background
|
|
5993
6478
|
*/
|
|
5994
|
-
async triggerRevalidation(
|
|
5995
|
-
const cacheKey = this.getCacheKey(
|
|
6479
|
+
async triggerRevalidation(path10, getStaticProps, render, params = {}, locale) {
|
|
6480
|
+
const cacheKey = this.getCacheKey(path10, locale);
|
|
5996
6481
|
if (this.isrState.revalidating.has(cacheKey)) {
|
|
5997
6482
|
return;
|
|
5998
6483
|
}
|
|
5999
6484
|
this.isrState.revalidating.add(cacheKey);
|
|
6000
6485
|
try {
|
|
6001
|
-
await this.generatePage(
|
|
6486
|
+
await this.generatePage(path10, getStaticProps, render, params, locale);
|
|
6002
6487
|
this.isrState.lastRevalidation.set(cacheKey, Date.now());
|
|
6003
6488
|
} finally {
|
|
6004
6489
|
this.isrState.revalidating.delete(cacheKey);
|
|
@@ -6007,9 +6492,9 @@ var SSGEngine = class {
|
|
|
6007
6492
|
/**
|
|
6008
6493
|
* Force revalidation of a path (On-Demand ISR)
|
|
6009
6494
|
*/
|
|
6010
|
-
async revalidate(
|
|
6495
|
+
async revalidate(path10, getStaticProps, render, params = {}, locale) {
|
|
6011
6496
|
try {
|
|
6012
|
-
const result = await this.generatePage(
|
|
6497
|
+
const result = await this.generatePage(path10, getStaticProps, render, params, locale);
|
|
6013
6498
|
return { revalidated: result.success, error: result.error?.message };
|
|
6014
6499
|
} catch (error2) {
|
|
6015
6500
|
return {
|
|
@@ -6172,10 +6657,10 @@ function createSSGHandler(options) {
|
|
|
6172
6657
|
const engine = getSSGEngine();
|
|
6173
6658
|
return async (req) => {
|
|
6174
6659
|
const url = new URL(req.url);
|
|
6175
|
-
const
|
|
6660
|
+
const path10 = url.pathname;
|
|
6176
6661
|
const params = {};
|
|
6177
6662
|
const result = await engine.getPage(
|
|
6178
|
-
|
|
6663
|
+
path10,
|
|
6179
6664
|
options.getStaticProps,
|
|
6180
6665
|
options.render,
|
|
6181
6666
|
params,
|
|
@@ -6542,7 +7027,7 @@ var AnalyticsEngine = class {
|
|
|
6542
7027
|
for (const pv of recentPageviews) {
|
|
6543
7028
|
pathCounts[pv.pathname] = (pathCounts[pv.pathname] || 0) + 1;
|
|
6544
7029
|
}
|
|
6545
|
-
const topPages = Object.entries(pathCounts).map(([
|
|
7030
|
+
const topPages = Object.entries(pathCounts).map(([path10, count]) => ({ path: path10, count })).sort((a, b) => b.count - a.count).slice(0, 10);
|
|
6546
7031
|
return {
|
|
6547
7032
|
activeUsers: activeSessions.size,
|
|
6548
7033
|
pageviews: recentPageviews.length,
|