@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/cli/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
4
10
|
var __esm = (fn, res) => function __init() {
|
|
5
11
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
12
|
};
|
|
@@ -730,7 +736,7 @@ function generateAPIResponse(state) {
|
|
|
730
736
|
function createDevDashboard(options = {}) {
|
|
731
737
|
const {
|
|
732
738
|
enabled = process.env.NODE_ENV !== "production",
|
|
733
|
-
path:
|
|
739
|
+
path: path10 = "/__float",
|
|
734
740
|
auth
|
|
735
741
|
} = options;
|
|
736
742
|
if (!enabled) {
|
|
@@ -738,7 +744,7 @@ function createDevDashboard(options = {}) {
|
|
|
738
744
|
}
|
|
739
745
|
return (req, res, next) => {
|
|
740
746
|
const url = req.url || "";
|
|
741
|
-
if (!url.startsWith(
|
|
747
|
+
if (!url.startsWith(path10)) {
|
|
742
748
|
return next();
|
|
743
749
|
}
|
|
744
750
|
if (auth) {
|
|
@@ -757,7 +763,7 @@ function createDevDashboard(options = {}) {
|
|
|
757
763
|
return;
|
|
758
764
|
}
|
|
759
765
|
}
|
|
760
|
-
const subPath = url.slice(
|
|
766
|
+
const subPath = url.slice(path10.length);
|
|
761
767
|
if (subPath === "" || subPath === "/") {
|
|
762
768
|
res.setHeader("Content-Type", "text/html");
|
|
763
769
|
res.end(generateDashboardHTML(dashboardState));
|
|
@@ -868,14 +874,14 @@ var init_devtools = __esm({
|
|
|
868
874
|
// src/cli/index.ts
|
|
869
875
|
init_esm_shims();
|
|
870
876
|
import { cac } from "cac";
|
|
871
|
-
import
|
|
877
|
+
import pc5 from "picocolors";
|
|
872
878
|
|
|
873
879
|
// src/server/dev-server.ts
|
|
874
880
|
init_esm_shims();
|
|
875
881
|
import http from "http";
|
|
876
|
-
import
|
|
877
|
-
import
|
|
878
|
-
import
|
|
882
|
+
import fs5 from "fs";
|
|
883
|
+
import path7 from "path";
|
|
884
|
+
import pc2 from "picocolors";
|
|
879
885
|
import chokidar from "chokidar";
|
|
880
886
|
import { WebSocketServer, WebSocket } from "ws";
|
|
881
887
|
import mime from "mime-types";
|
|
@@ -937,6 +943,26 @@ function findLayouts(routePath, allLayouts) {
|
|
|
937
943
|
}
|
|
938
944
|
return layouts;
|
|
939
945
|
}
|
|
946
|
+
function findLoading(routePath, allLoading) {
|
|
947
|
+
const segments = routePath.split("/").filter(Boolean);
|
|
948
|
+
for (let i = segments.length; i >= 0; i--) {
|
|
949
|
+
const currentPath = i === 0 ? "/" : "/" + segments.slice(0, i).join("/");
|
|
950
|
+
if (allLoading.has(currentPath)) {
|
|
951
|
+
return allLoading.get(currentPath);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return void 0;
|
|
955
|
+
}
|
|
956
|
+
function findError(routePath, allErrors) {
|
|
957
|
+
const segments = routePath.split("/").filter(Boolean);
|
|
958
|
+
for (let i = segments.length; i >= 0; i--) {
|
|
959
|
+
const currentPath = i === 0 ? "/" : "/" + segments.slice(0, i).join("/");
|
|
960
|
+
if (allErrors.has(currentPath)) {
|
|
961
|
+
return allErrors.get(currentPath);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
return void 0;
|
|
965
|
+
}
|
|
940
966
|
async function scanRoutes(rootDir, options = {}) {
|
|
941
967
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
942
968
|
const appDir = path2.join(rootDir, opts.appDir);
|
|
@@ -948,12 +974,20 @@ async function scanRoutes(rootDir, options = {}) {
|
|
|
948
974
|
ignore: ["**/node_modules/**", "**/_*/**"]
|
|
949
975
|
});
|
|
950
976
|
const layoutMap = /* @__PURE__ */ new Map();
|
|
977
|
+
const loadingMap = /* @__PURE__ */ new Map();
|
|
978
|
+
const errorMap = /* @__PURE__ */ new Map();
|
|
951
979
|
for (const file of files) {
|
|
952
980
|
const type = getRouteType(file);
|
|
981
|
+
const { urlPath } = filePathToUrlPath(file, "");
|
|
953
982
|
if (type === "layout") {
|
|
954
|
-
const { urlPath } = filePathToUrlPath(file, "");
|
|
955
983
|
const layoutPath = urlPath === "/" ? "/" : urlPath.replace(/\/layout$/, "") || "/";
|
|
956
984
|
layoutMap.set(layoutPath, path2.join(appDir, file));
|
|
985
|
+
} else if (type === "loading") {
|
|
986
|
+
const loadingPath = urlPath === "/" ? "/" : urlPath.replace(/\/loading$/, "") || "/";
|
|
987
|
+
loadingMap.set(loadingPath, path2.join(appDir, file));
|
|
988
|
+
} else if (type === "error") {
|
|
989
|
+
const errorPath = urlPath === "/" ? "/" : urlPath.replace(/\/error$/, "") || "/";
|
|
990
|
+
errorMap.set(errorPath, path2.join(appDir, file));
|
|
957
991
|
}
|
|
958
992
|
}
|
|
959
993
|
const routes = [];
|
|
@@ -971,7 +1005,9 @@ async function scanRoutes(rootDir, options = {}) {
|
|
|
971
1005
|
params,
|
|
972
1006
|
isCatchAll,
|
|
973
1007
|
isOptionalCatchAll,
|
|
974
|
-
layouts: type === "page" ? findLayouts(urlPath, layoutMap) : []
|
|
1008
|
+
layouts: type === "page" ? findLayouts(urlPath, layoutMap) : [],
|
|
1009
|
+
loading: type === "page" ? findLoading(urlPath, loadingMap) : void 0,
|
|
1010
|
+
error: type === "page" ? findError(urlPath, errorMap) : void 0
|
|
975
1011
|
};
|
|
976
1012
|
routes.push(route);
|
|
977
1013
|
}
|
|
@@ -1029,23 +1065,222 @@ import { Writable } from "stream";
|
|
|
1029
1065
|
// src/build/transform.ts
|
|
1030
1066
|
init_esm_shims();
|
|
1031
1067
|
import * as esbuild from "esbuild";
|
|
1068
|
+
import fs2 from "fs";
|
|
1069
|
+
import path4 from "path";
|
|
1070
|
+
import { pathToFileURL } from "url";
|
|
1071
|
+
|
|
1072
|
+
// src/build/persistent-cache.ts
|
|
1073
|
+
init_esm_shims();
|
|
1032
1074
|
import fs from "fs";
|
|
1033
1075
|
import path3 from "path";
|
|
1034
|
-
import
|
|
1076
|
+
import crypto from "crypto";
|
|
1077
|
+
var PersistentCache = class {
|
|
1078
|
+
cacheDir;
|
|
1079
|
+
manifestPath;
|
|
1080
|
+
manifest;
|
|
1081
|
+
constructor(rootDir = process.cwd()) {
|
|
1082
|
+
this.cacheDir = path3.join(rootDir, ".float", "cache");
|
|
1083
|
+
this.manifestPath = path3.join(this.cacheDir, "manifest.json");
|
|
1084
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
1085
|
+
this.manifest = this.loadManifest();
|
|
1086
|
+
}
|
|
1087
|
+
loadManifest() {
|
|
1088
|
+
if (fs.existsSync(this.manifestPath)) {
|
|
1089
|
+
try {
|
|
1090
|
+
const data = fs.readFileSync(this.manifestPath, "utf-8");
|
|
1091
|
+
return JSON.parse(data);
|
|
1092
|
+
} catch {
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
return {
|
|
1096
|
+
version: "1.0",
|
|
1097
|
+
entries: {}
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
saveManifest() {
|
|
1101
|
+
fs.writeFileSync(
|
|
1102
|
+
this.manifestPath,
|
|
1103
|
+
JSON.stringify(this.manifest, null, 2)
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Generate hash for content
|
|
1108
|
+
*/
|
|
1109
|
+
hash(content) {
|
|
1110
|
+
return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Get cache key path
|
|
1114
|
+
*/
|
|
1115
|
+
getKeyPath(key) {
|
|
1116
|
+
const safeKey = key.replace(/[^a-z0-9_-]/gi, "_");
|
|
1117
|
+
return path3.join(this.cacheDir, `${safeKey}.cache`);
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Check if cache entry is valid
|
|
1121
|
+
*/
|
|
1122
|
+
has(key, contentHash) {
|
|
1123
|
+
const entry = this.manifest.entries[key];
|
|
1124
|
+
if (!entry) return false;
|
|
1125
|
+
const cachePath = this.getKeyPath(key);
|
|
1126
|
+
if (!fs.existsSync(cachePath)) {
|
|
1127
|
+
delete this.manifest.entries[key];
|
|
1128
|
+
this.saveManifest();
|
|
1129
|
+
return false;
|
|
1130
|
+
}
|
|
1131
|
+
if (contentHash && entry.hash !== contentHash) {
|
|
1132
|
+
return false;
|
|
1133
|
+
}
|
|
1134
|
+
return true;
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Get cached value
|
|
1138
|
+
*/
|
|
1139
|
+
get(key) {
|
|
1140
|
+
if (!this.has(key)) return null;
|
|
1141
|
+
try {
|
|
1142
|
+
const cachePath = this.getKeyPath(key);
|
|
1143
|
+
const data = fs.readFileSync(cachePath, "utf-8");
|
|
1144
|
+
const entry = JSON.parse(data);
|
|
1145
|
+
return entry.value;
|
|
1146
|
+
} catch {
|
|
1147
|
+
return null;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Set cache value
|
|
1152
|
+
*/
|
|
1153
|
+
set(key, value, content) {
|
|
1154
|
+
const entry = {
|
|
1155
|
+
key,
|
|
1156
|
+
value,
|
|
1157
|
+
timestamp: Date.now(),
|
|
1158
|
+
hash: content ? this.hash(content) : this.hash(JSON.stringify(value))
|
|
1159
|
+
};
|
|
1160
|
+
const cachePath = this.getKeyPath(key);
|
|
1161
|
+
const data = JSON.stringify(entry);
|
|
1162
|
+
fs.writeFileSync(cachePath, data);
|
|
1163
|
+
this.manifest.entries[key] = {
|
|
1164
|
+
hash: entry.hash,
|
|
1165
|
+
timestamp: entry.timestamp,
|
|
1166
|
+
size: Buffer.byteLength(data)
|
|
1167
|
+
};
|
|
1168
|
+
this.saveManifest();
|
|
1169
|
+
}
|
|
1170
|
+
/**
|
|
1171
|
+
* Delete cache entry
|
|
1172
|
+
*/
|
|
1173
|
+
delete(key) {
|
|
1174
|
+
const cachePath = this.getKeyPath(key);
|
|
1175
|
+
if (fs.existsSync(cachePath)) {
|
|
1176
|
+
fs.unlinkSync(cachePath);
|
|
1177
|
+
}
|
|
1178
|
+
if (this.manifest.entries[key]) {
|
|
1179
|
+
delete this.manifest.entries[key];
|
|
1180
|
+
this.saveManifest();
|
|
1181
|
+
return true;
|
|
1182
|
+
}
|
|
1183
|
+
return false;
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* Clear all cache
|
|
1187
|
+
*/
|
|
1188
|
+
clear() {
|
|
1189
|
+
if (fs.existsSync(this.cacheDir)) {
|
|
1190
|
+
fs.rmSync(this.cacheDir, { recursive: true });
|
|
1191
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
1192
|
+
}
|
|
1193
|
+
this.manifest = {
|
|
1194
|
+
version: "1.0",
|
|
1195
|
+
entries: {}
|
|
1196
|
+
};
|
|
1197
|
+
this.saveManifest();
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Get cache statistics
|
|
1201
|
+
*/
|
|
1202
|
+
stats() {
|
|
1203
|
+
const entries = Object.values(this.manifest.entries);
|
|
1204
|
+
const size = entries.reduce((acc, e) => acc + e.size, 0);
|
|
1205
|
+
const timestamps = entries.map((e) => e.timestamp);
|
|
1206
|
+
return {
|
|
1207
|
+
entries: entries.length,
|
|
1208
|
+
size,
|
|
1209
|
+
oldestEntry: timestamps.length > 0 ? Math.min(...timestamps) : null,
|
|
1210
|
+
newestEntry: timestamps.length > 0 ? Math.max(...timestamps) : null
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Clean old entries (older than maxAge milliseconds)
|
|
1215
|
+
*/
|
|
1216
|
+
prune(maxAge = 7 * 24 * 60 * 60 * 1e3) {
|
|
1217
|
+
const now = Date.now();
|
|
1218
|
+
let cleaned = 0;
|
|
1219
|
+
for (const [key, entry] of Object.entries(this.manifest.entries)) {
|
|
1220
|
+
if (now - entry.timestamp > maxAge) {
|
|
1221
|
+
this.delete(key);
|
|
1222
|
+
cleaned++;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
return cleaned;
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
var globalCache = null;
|
|
1229
|
+
function getCache(rootDir) {
|
|
1230
|
+
if (!globalCache) {
|
|
1231
|
+
globalCache = new PersistentCache(rootDir);
|
|
1232
|
+
}
|
|
1233
|
+
return globalCache;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// src/build/transform.ts
|
|
1035
1237
|
var moduleCache = /* @__PURE__ */ new Map();
|
|
1036
|
-
async function transformFile(filePath) {
|
|
1037
|
-
const absolutePath =
|
|
1038
|
-
if (!
|
|
1238
|
+
async function transformFile(filePath, useCache = true) {
|
|
1239
|
+
const absolutePath = path4.isAbsolute(filePath) ? filePath : path4.resolve(filePath);
|
|
1240
|
+
if (!fs2.existsSync(absolutePath)) {
|
|
1039
1241
|
throw new Error(`File not found: ${absolutePath}`);
|
|
1040
1242
|
}
|
|
1041
|
-
const stats =
|
|
1243
|
+
const stats = fs2.statSync(absolutePath);
|
|
1042
1244
|
const mtime = stats.mtimeMs;
|
|
1043
1245
|
const cached = moduleCache.get(absolutePath);
|
|
1044
1246
|
if (cached && cached.mtime === mtime) {
|
|
1045
1247
|
return cached.module;
|
|
1046
1248
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1249
|
+
if (useCache) {
|
|
1250
|
+
const cache = getCache();
|
|
1251
|
+
const source2 = fs2.readFileSync(absolutePath, "utf-8");
|
|
1252
|
+
const sourceHash = __require("crypto").createHash("sha256").update(source2).digest("hex").slice(0, 16);
|
|
1253
|
+
const cacheKey = `transform_${absolutePath}_${sourceHash}`;
|
|
1254
|
+
if (cache.has(cacheKey)) {
|
|
1255
|
+
const cachedCode = cache.get(cacheKey);
|
|
1256
|
+
if (cachedCode) {
|
|
1257
|
+
const tempDir2 = path4.join(process.cwd(), ".float", ".cache");
|
|
1258
|
+
fs2.mkdirSync(tempDir2, { recursive: true });
|
|
1259
|
+
const tempFile2 = path4.join(tempDir2, `${path4.basename(absolutePath, path4.extname(absolutePath))}_${Date.now()}.mjs`);
|
|
1260
|
+
fs2.writeFileSync(tempFile2, cachedCode);
|
|
1261
|
+
try {
|
|
1262
|
+
const module = await import(pathToFileURL(tempFile2).href);
|
|
1263
|
+
moduleCache.set(absolutePath, { module, mtime });
|
|
1264
|
+
setImmediate(() => {
|
|
1265
|
+
try {
|
|
1266
|
+
fs2.unlinkSync(tempFile2);
|
|
1267
|
+
} catch {
|
|
1268
|
+
}
|
|
1269
|
+
});
|
|
1270
|
+
return module;
|
|
1271
|
+
} catch (error) {
|
|
1272
|
+
setImmediate(() => {
|
|
1273
|
+
try {
|
|
1274
|
+
fs2.unlinkSync(tempFile2);
|
|
1275
|
+
} catch {
|
|
1276
|
+
}
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
const source = fs2.readFileSync(absolutePath, "utf-8");
|
|
1283
|
+
const ext = path4.extname(absolutePath);
|
|
1049
1284
|
const loader = getLoader(ext);
|
|
1050
1285
|
const result = await esbuild.transform(source, {
|
|
1051
1286
|
loader,
|
|
@@ -1055,25 +1290,31 @@ async function transformFile(filePath) {
|
|
|
1055
1290
|
sourcemap: "inline",
|
|
1056
1291
|
sourcefile: absolutePath
|
|
1057
1292
|
});
|
|
1058
|
-
const tempDir =
|
|
1059
|
-
|
|
1060
|
-
const tempFile =
|
|
1293
|
+
const tempDir = path4.join(process.cwd(), ".float", ".cache");
|
|
1294
|
+
fs2.mkdirSync(tempDir, { recursive: true });
|
|
1295
|
+
const tempFile = path4.join(tempDir, `${path4.basename(absolutePath, ext)}_${Date.now()}.mjs`);
|
|
1061
1296
|
let code = result.code;
|
|
1062
|
-
code = rewriteImports(code,
|
|
1063
|
-
|
|
1297
|
+
code = rewriteImports(code, path4.dirname(absolutePath));
|
|
1298
|
+
fs2.writeFileSync(tempFile, code);
|
|
1064
1299
|
try {
|
|
1065
1300
|
const module = await import(pathToFileURL(tempFile).href);
|
|
1066
1301
|
moduleCache.set(absolutePath, { module, mtime });
|
|
1302
|
+
if (useCache) {
|
|
1303
|
+
const cache = getCache();
|
|
1304
|
+
const sourceHash = __require("crypto").createHash("sha256").update(source).digest("hex").slice(0, 16);
|
|
1305
|
+
const cacheKey = `transform_${absolutePath}_${sourceHash}`;
|
|
1306
|
+
cache.set(cacheKey, code, source);
|
|
1307
|
+
}
|
|
1067
1308
|
setImmediate(() => {
|
|
1068
1309
|
try {
|
|
1069
|
-
|
|
1310
|
+
fs2.unlinkSync(tempFile);
|
|
1070
1311
|
} catch {
|
|
1071
1312
|
}
|
|
1072
1313
|
});
|
|
1073
1314
|
return module;
|
|
1074
1315
|
} catch (error) {
|
|
1075
1316
|
try {
|
|
1076
|
-
|
|
1317
|
+
fs2.unlinkSync(tempFile);
|
|
1077
1318
|
} catch {
|
|
1078
1319
|
}
|
|
1079
1320
|
throw error;
|
|
@@ -1102,18 +1343,18 @@ function getLoader(ext) {
|
|
|
1102
1343
|
function rewriteImports(code, baseDir) {
|
|
1103
1344
|
const importRegex = /from\s+['"](\.[^'"]+)['"]/g;
|
|
1104
1345
|
return code.replace(importRegex, (match, importPath) => {
|
|
1105
|
-
let resolvedPath =
|
|
1346
|
+
let resolvedPath = path4.resolve(baseDir, importPath);
|
|
1106
1347
|
const extensions = [".tsx", ".ts", ".jsx", ".js", ".mjs", ""];
|
|
1107
1348
|
let found = false;
|
|
1108
1349
|
for (const ext of extensions) {
|
|
1109
1350
|
const tryPath = resolvedPath + ext;
|
|
1110
|
-
if (
|
|
1351
|
+
if (fs2.existsSync(tryPath)) {
|
|
1111
1352
|
resolvedPath = tryPath;
|
|
1112
1353
|
found = true;
|
|
1113
1354
|
break;
|
|
1114
1355
|
}
|
|
1115
|
-
const indexPath =
|
|
1116
|
-
if (
|
|
1356
|
+
const indexPath = path4.join(resolvedPath, `index${ext}`);
|
|
1357
|
+
if (fs2.existsSync(indexPath)) {
|
|
1117
1358
|
resolvedPath = indexPath;
|
|
1118
1359
|
found = true;
|
|
1119
1360
|
break;
|
|
@@ -2464,11 +2705,214 @@ function generateExamplesPage() {
|
|
|
2464
2705
|
` + footer;
|
|
2465
2706
|
}
|
|
2466
2707
|
|
|
2708
|
+
// src/build/css-processor.ts
|
|
2709
|
+
init_esm_shims();
|
|
2710
|
+
import fs4 from "fs";
|
|
2711
|
+
import path6 from "path";
|
|
2712
|
+
|
|
2713
|
+
// src/build/tailwind-setup.ts
|
|
2714
|
+
init_esm_shims();
|
|
2715
|
+
import fs3 from "fs";
|
|
2716
|
+
import path5 from "path";
|
|
2717
|
+
import pc from "picocolors";
|
|
2718
|
+
function checkTailwindSetup(rootDir) {
|
|
2719
|
+
const possibleConfigs = [
|
|
2720
|
+
"tailwind.config.js",
|
|
2721
|
+
"tailwind.config.ts",
|
|
2722
|
+
"tailwind.config.mjs",
|
|
2723
|
+
"tailwind.config.cjs"
|
|
2724
|
+
];
|
|
2725
|
+
let configPath = null;
|
|
2726
|
+
for (const config of possibleConfigs) {
|
|
2727
|
+
const fullPath = path5.join(rootDir, config);
|
|
2728
|
+
if (fs3.existsSync(fullPath)) {
|
|
2729
|
+
configPath = fullPath;
|
|
2730
|
+
break;
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
const globalsPath = path5.join(rootDir, "app", "globals.css");
|
|
2734
|
+
const hasGlobals = fs3.existsSync(globalsPath);
|
|
2735
|
+
return {
|
|
2736
|
+
hasTailwind: !!configPath,
|
|
2737
|
+
configPath,
|
|
2738
|
+
globalsPath: hasGlobals ? globalsPath : null,
|
|
2739
|
+
needsSetup: !configPath || !hasGlobals
|
|
2740
|
+
};
|
|
2741
|
+
}
|
|
2742
|
+
async function setupTailwind(rootDir, options = {}) {
|
|
2743
|
+
const { force = false, silent = false } = options;
|
|
2744
|
+
const config = checkTailwindSetup(rootDir);
|
|
2745
|
+
if (!force && !config.needsSetup) {
|
|
2746
|
+
return;
|
|
2747
|
+
}
|
|
2748
|
+
if (!silent) {
|
|
2749
|
+
console.log(pc.cyan("\n\u{1F3A8} Setting up Tailwind CSS..."));
|
|
2750
|
+
}
|
|
2751
|
+
if (!config.configPath || force) {
|
|
2752
|
+
const tailwindConfig = `/** @type {import('tailwindcss').Config} */
|
|
2753
|
+
export default {
|
|
2754
|
+
content: [
|
|
2755
|
+
'./app/**/*.{js,ts,jsx,tsx}',
|
|
2756
|
+
'./components/**/*.{js,ts,jsx,tsx}',
|
|
2757
|
+
],
|
|
2758
|
+
theme: {
|
|
2759
|
+
extend: {},
|
|
2760
|
+
},
|
|
2761
|
+
plugins: [],
|
|
2762
|
+
}
|
|
2763
|
+
`;
|
|
2764
|
+
const configPath = path5.join(rootDir, "tailwind.config.js");
|
|
2765
|
+
fs3.writeFileSync(configPath, tailwindConfig);
|
|
2766
|
+
if (!silent) {
|
|
2767
|
+
console.log(pc.green(" \u2713 Created tailwind.config.js"));
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
const postcssPath = path5.join(rootDir, "postcss.config.js");
|
|
2771
|
+
if (!fs3.existsSync(postcssPath) || force) {
|
|
2772
|
+
const postcssConfig = `export default {
|
|
2773
|
+
plugins: {
|
|
2774
|
+
tailwindcss: {},
|
|
2775
|
+
autoprefixer: {},
|
|
2776
|
+
},
|
|
2777
|
+
}
|
|
2778
|
+
`;
|
|
2779
|
+
fs3.writeFileSync(postcssPath, postcssConfig);
|
|
2780
|
+
if (!silent) {
|
|
2781
|
+
console.log(pc.green(" \u2713 Created postcss.config.js"));
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
const appDir = path5.join(rootDir, "app");
|
|
2785
|
+
if (!fs3.existsSync(appDir)) {
|
|
2786
|
+
fs3.mkdirSync(appDir, { recursive: true });
|
|
2787
|
+
}
|
|
2788
|
+
const globalsPath = path5.join(appDir, "globals.css");
|
|
2789
|
+
if (!fs3.existsSync(globalsPath) || force) {
|
|
2790
|
+
const globalsCss = `@tailwind base;
|
|
2791
|
+
@tailwind components;
|
|
2792
|
+
@tailwind utilities;
|
|
2793
|
+
`;
|
|
2794
|
+
fs3.writeFileSync(globalsPath, globalsCss);
|
|
2795
|
+
if (!silent) {
|
|
2796
|
+
console.log(pc.green(" \u2713 Created app/globals.css"));
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
const layoutPath = path5.join(appDir, "layout.tsx");
|
|
2800
|
+
if (!fs3.existsSync(layoutPath)) {
|
|
2801
|
+
const layoutContent = `import './globals.css'
|
|
2802
|
+
|
|
2803
|
+
export default function RootLayout({
|
|
2804
|
+
children,
|
|
2805
|
+
}: {
|
|
2806
|
+
children: React.ReactNode
|
|
2807
|
+
}) {
|
|
2808
|
+
return (
|
|
2809
|
+
<html lang="en">
|
|
2810
|
+
<body>{children}</body>
|
|
2811
|
+
</html>
|
|
2812
|
+
)
|
|
2813
|
+
}
|
|
2814
|
+
`;
|
|
2815
|
+
fs3.writeFileSync(layoutPath, layoutContent);
|
|
2816
|
+
if (!silent) {
|
|
2817
|
+
console.log(pc.green(" \u2713 Created app/layout.tsx"));
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
if (!silent) {
|
|
2821
|
+
console.log(pc.green("\n\u2728 Tailwind CSS ready!\n"));
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
function checkTailwindDeps(rootDir) {
|
|
2825
|
+
const packageJsonPath = path5.join(rootDir, "package.json");
|
|
2826
|
+
if (!fs3.existsSync(packageJsonPath)) {
|
|
2827
|
+
return {
|
|
2828
|
+
hasPackageJson: false,
|
|
2829
|
+
hasTailwind: false,
|
|
2830
|
+
hasPostCSS: false,
|
|
2831
|
+
hasAutoprefixer: false
|
|
2832
|
+
};
|
|
2833
|
+
}
|
|
2834
|
+
const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
|
|
2835
|
+
const allDeps = {
|
|
2836
|
+
...packageJson.dependencies,
|
|
2837
|
+
...packageJson.devDependencies
|
|
2838
|
+
};
|
|
2839
|
+
return {
|
|
2840
|
+
hasPackageJson: true,
|
|
2841
|
+
hasTailwind: !!allDeps["tailwindcss"],
|
|
2842
|
+
hasPostCSS: !!allDeps["postcss"],
|
|
2843
|
+
hasAutoprefixer: !!allDeps["autoprefixer"]
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
function getTailwindInstallCommand(rootDir) {
|
|
2847
|
+
const deps = checkTailwindDeps(rootDir);
|
|
2848
|
+
if (!deps.hasPackageJson) {
|
|
2849
|
+
return null;
|
|
2850
|
+
}
|
|
2851
|
+
const missing = [];
|
|
2852
|
+
if (!deps.hasTailwind) missing.push("tailwindcss");
|
|
2853
|
+
if (!deps.hasPostCSS) missing.push("postcss");
|
|
2854
|
+
if (!deps.hasAutoprefixer) missing.push("autoprefixer");
|
|
2855
|
+
if (missing.length === 0) {
|
|
2856
|
+
return null;
|
|
2857
|
+
}
|
|
2858
|
+
const hasYarnLock = fs3.existsSync(path5.join(rootDir, "yarn.lock"));
|
|
2859
|
+
const hasPnpmLock = fs3.existsSync(path5.join(rootDir, "pnpm-lock.yaml"));
|
|
2860
|
+
const hasBunLock = fs3.existsSync(path5.join(rootDir, "bun.lockb"));
|
|
2861
|
+
let pm = "npm install -D";
|
|
2862
|
+
if (hasBunLock) pm = "bun add -d";
|
|
2863
|
+
else if (hasPnpmLock) pm = "pnpm add -D";
|
|
2864
|
+
else if (hasYarnLock) pm = "yarn add -D";
|
|
2865
|
+
return `${pm} ${missing.join(" ")}`;
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
// src/build/css-processor.ts
|
|
2869
|
+
async function processCSS(filePath, rootDir = process.cwd()) {
|
|
2870
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
2871
|
+
const tailwindConfig = checkTailwindSetup(rootDir);
|
|
2872
|
+
if (!tailwindConfig.hasTailwind) {
|
|
2873
|
+
return { code: content };
|
|
2874
|
+
}
|
|
2875
|
+
try {
|
|
2876
|
+
const postcss = await import("postcss").then((m) => m.default);
|
|
2877
|
+
const tailwindcss = await import("tailwindcss").then((m) => m.default);
|
|
2878
|
+
const autoprefixer = await import("autoprefixer").then((m) => m.default);
|
|
2879
|
+
const configPath = tailwindConfig.configPath || path6.join(rootDir, "tailwind.config.js");
|
|
2880
|
+
let tailwindConfigModule = {};
|
|
2881
|
+
if (fs4.existsSync(configPath)) {
|
|
2882
|
+
const configUrl = new URL(`file://${configPath}`);
|
|
2883
|
+
tailwindConfigModule = await import(configUrl.href).then((m) => m.default || m);
|
|
2884
|
+
}
|
|
2885
|
+
const result = await postcss([
|
|
2886
|
+
tailwindcss(tailwindConfigModule),
|
|
2887
|
+
autoprefixer()
|
|
2888
|
+
]).process(content, {
|
|
2889
|
+
from: filePath,
|
|
2890
|
+
to: filePath,
|
|
2891
|
+
map: { inline: false }
|
|
2892
|
+
});
|
|
2893
|
+
return {
|
|
2894
|
+
code: result.css,
|
|
2895
|
+
map: result.map?.toString()
|
|
2896
|
+
};
|
|
2897
|
+
} catch (error) {
|
|
2898
|
+
console.warn("CSS processing failed, serving raw CSS:", error);
|
|
2899
|
+
return { code: content };
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
function needsCSSProcessing(filePath, rootDir) {
|
|
2903
|
+
const config = checkTailwindSetup(rootDir);
|
|
2904
|
+
if (!config.hasTailwind) {
|
|
2905
|
+
return false;
|
|
2906
|
+
}
|
|
2907
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
2908
|
+
return content.includes("@tailwind") || content.includes("@apply");
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2467
2911
|
// src/server/dev-server.ts
|
|
2468
2912
|
async function createDevServer(options) {
|
|
2469
2913
|
const { port, host, open } = options;
|
|
2470
2914
|
const rootDir = process.cwd();
|
|
2471
|
-
const publicDir =
|
|
2915
|
+
const publicDir = path7.join(rootDir, "public");
|
|
2472
2916
|
let routes = [];
|
|
2473
2917
|
let server = null;
|
|
2474
2918
|
let wss = null;
|
|
@@ -2476,9 +2920,20 @@ async function createDevServer(options) {
|
|
|
2476
2920
|
async function refreshRoutes() {
|
|
2477
2921
|
try {
|
|
2478
2922
|
routes = await scanRoutes(rootDir);
|
|
2479
|
-
console.log(
|
|
2923
|
+
console.log(pc2.dim(` \u{1F4C1} Found ${routes.length} routes`));
|
|
2924
|
+
const tailwindConfig = checkTailwindSetup(rootDir);
|
|
2925
|
+
if (tailwindConfig.needsSetup) {
|
|
2926
|
+
console.log(pc2.yellow(" \u26A0\uFE0F Tailwind not configured"));
|
|
2927
|
+
const installCmd = getTailwindInstallCommand(rootDir);
|
|
2928
|
+
if (installCmd) {
|
|
2929
|
+
console.log(pc2.dim(` \u{1F4A1} Run: ${installCmd}`));
|
|
2930
|
+
console.log(pc2.dim(` \u{1F4A1} Then: npx float dev`));
|
|
2931
|
+
} else {
|
|
2932
|
+
await setupTailwind(rootDir);
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2480
2935
|
} catch (error) {
|
|
2481
|
-
console.error(
|
|
2936
|
+
console.error(pc2.red("Failed to scan routes:"), error);
|
|
2482
2937
|
}
|
|
2483
2938
|
}
|
|
2484
2939
|
function notifyClients(type, data) {
|
|
@@ -2539,16 +2994,46 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2539
2994
|
async function handleRequest(req, res) {
|
|
2540
2995
|
const url = new URL(req.url || "/", `http://${host}:${port}`);
|
|
2541
2996
|
const pathname = url.pathname;
|
|
2542
|
-
console.log(
|
|
2997
|
+
console.log(pc2.dim(` ${req.method} ${pathname}`));
|
|
2543
2998
|
try {
|
|
2544
|
-
const publicFilePath =
|
|
2545
|
-
if (
|
|
2546
|
-
const content =
|
|
2999
|
+
const publicFilePath = path7.join(publicDir, pathname);
|
|
3000
|
+
if (fs5.existsSync(publicFilePath) && fs5.statSync(publicFilePath).isFile()) {
|
|
3001
|
+
const content = fs5.readFileSync(publicFilePath);
|
|
2547
3002
|
const contentType = mime.lookup(publicFilePath) || "application/octet-stream";
|
|
2548
3003
|
res.writeHead(200, { "Content-Type": contentType });
|
|
2549
3004
|
res.end(content);
|
|
2550
3005
|
return;
|
|
2551
3006
|
}
|
|
3007
|
+
if (pathname.endsWith(".css")) {
|
|
3008
|
+
const cssPath = path7.join(rootDir, "app", pathname.replace(/^\//, ""));
|
|
3009
|
+
if (fs5.existsSync(cssPath)) {
|
|
3010
|
+
try {
|
|
3011
|
+
const needsProcessing = needsCSSProcessing(cssPath, rootDir);
|
|
3012
|
+
if (needsProcessing) {
|
|
3013
|
+
const result = await processCSS(cssPath, rootDir);
|
|
3014
|
+
res.writeHead(200, {
|
|
3015
|
+
"Content-Type": "text/css",
|
|
3016
|
+
"Cache-Control": "no-cache"
|
|
3017
|
+
});
|
|
3018
|
+
res.end(result.code);
|
|
3019
|
+
} else {
|
|
3020
|
+
const content = fs5.readFileSync(cssPath, "utf-8");
|
|
3021
|
+
res.writeHead(200, {
|
|
3022
|
+
"Content-Type": "text/css",
|
|
3023
|
+
"Cache-Control": "no-cache"
|
|
3024
|
+
});
|
|
3025
|
+
res.end(content);
|
|
3026
|
+
}
|
|
3027
|
+
return;
|
|
3028
|
+
} catch (error) {
|
|
3029
|
+
console.error(pc2.red("CSS processing error:"), error);
|
|
3030
|
+
const content = fs5.readFileSync(cssPath, "utf-8");
|
|
3031
|
+
res.writeHead(200, { "Content-Type": "text/css" });
|
|
3032
|
+
res.end(content);
|
|
3033
|
+
return;
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
2552
3037
|
if (pathname.startsWith("/_float/")) {
|
|
2553
3038
|
res.writeHead(200, { "Content-Type": "application/javascript" });
|
|
2554
3039
|
res.end("// Float.js internal asset");
|
|
@@ -2613,7 +3098,7 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2613
3098
|
});
|
|
2614
3099
|
res.end(html);
|
|
2615
3100
|
} catch (error) {
|
|
2616
|
-
console.error(
|
|
3101
|
+
console.error(pc2.red("Request error:"), error);
|
|
2617
3102
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
2618
3103
|
res.end(createErrorPage(error));
|
|
2619
3104
|
}
|
|
@@ -2642,7 +3127,7 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2642
3127
|
const responseBody = await response.text();
|
|
2643
3128
|
res.end(responseBody);
|
|
2644
3129
|
} catch (error) {
|
|
2645
|
-
console.error(
|
|
3130
|
+
console.error(pc2.red("API route error:"), error);
|
|
2646
3131
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
2647
3132
|
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
2648
3133
|
}
|
|
@@ -2657,9 +3142,9 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2657
3142
|
});
|
|
2658
3143
|
const watcher = chokidar.watch(
|
|
2659
3144
|
[
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
3145
|
+
path7.join(rootDir, "app/**/*.{ts,tsx,js,jsx}"),
|
|
3146
|
+
path7.join(rootDir, "components/**/*.{ts,tsx,js,jsx}"),
|
|
3147
|
+
path7.join(rootDir, "lib/**/*.{ts,tsx,js,jsx}")
|
|
2663
3148
|
],
|
|
2664
3149
|
{
|
|
2665
3150
|
ignored: /node_modules/,
|
|
@@ -2667,8 +3152,8 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2667
3152
|
}
|
|
2668
3153
|
);
|
|
2669
3154
|
watcher.on("change", async (filePath) => {
|
|
2670
|
-
console.log(
|
|
2671
|
-
\u26A1 File changed: ${
|
|
3155
|
+
console.log(pc2.yellow(`
|
|
3156
|
+
\u26A1 File changed: ${path7.relative(rootDir, filePath)}`));
|
|
2672
3157
|
if (filePath.includes("/app/")) {
|
|
2673
3158
|
await refreshRoutes();
|
|
2674
3159
|
}
|
|
@@ -2676,31 +3161,31 @@ ${FLOAT_ERROR_OVERLAY}
|
|
|
2676
3161
|
});
|
|
2677
3162
|
watcher.on("add", async (filePath) => {
|
|
2678
3163
|
if (filePath.includes("/app/")) {
|
|
2679
|
-
console.log(
|
|
2680
|
-
\u2795 File added: ${
|
|
3164
|
+
console.log(pc2.green(`
|
|
3165
|
+
\u2795 File added: ${path7.relative(rootDir, filePath)}`));
|
|
2681
3166
|
await refreshRoutes();
|
|
2682
3167
|
notifyClients("reload");
|
|
2683
3168
|
}
|
|
2684
3169
|
});
|
|
2685
3170
|
watcher.on("unlink", async (filePath) => {
|
|
2686
3171
|
if (filePath.includes("/app/")) {
|
|
2687
|
-
console.log(
|
|
2688
|
-
\u2796 File removed: ${
|
|
3172
|
+
console.log(pc2.red(`
|
|
3173
|
+
\u2796 File removed: ${path7.relative(rootDir, filePath)}`));
|
|
2689
3174
|
await refreshRoutes();
|
|
2690
3175
|
notifyClients("reload");
|
|
2691
3176
|
}
|
|
2692
3177
|
});
|
|
2693
3178
|
return new Promise((resolve, reject) => {
|
|
2694
3179
|
server.listen(port, host, () => {
|
|
2695
|
-
console.log(
|
|
2696
|
-
console.log(
|
|
3180
|
+
console.log(pc2.green(` \u2705 Server running at ${pc2.cyan(`http://${host}:${port}`)}`));
|
|
3181
|
+
console.log(pc2.dim(` \u26A1 HMR enabled on ws://${host}:${port + 1}
|
|
2697
3182
|
`));
|
|
2698
|
-
console.log(
|
|
3183
|
+
console.log(pc2.bold(" Routes:"));
|
|
2699
3184
|
routes.forEach((route) => {
|
|
2700
3185
|
if (route.type === "page") {
|
|
2701
|
-
console.log(
|
|
3186
|
+
console.log(pc2.dim(` ${pc2.green("\u25CF")} ${route.path}`));
|
|
2702
3187
|
} else if (route.type === "api") {
|
|
2703
|
-
console.log(
|
|
3188
|
+
console.log(pc2.dim(` ${pc2.blue("\u25C6")} ${route.path} (API)`));
|
|
2704
3189
|
}
|
|
2705
3190
|
});
|
|
2706
3191
|
console.log("");
|
|
@@ -2820,9 +3305,9 @@ function escapeHtml2(text) {
|
|
|
2820
3305
|
// src/build/index.ts
|
|
2821
3306
|
init_esm_shims();
|
|
2822
3307
|
import * as esbuild2 from "esbuild";
|
|
2823
|
-
import
|
|
2824
|
-
import
|
|
2825
|
-
import
|
|
3308
|
+
import fs6 from "fs";
|
|
3309
|
+
import path8 from "path";
|
|
3310
|
+
import pc3 from "picocolors";
|
|
2826
3311
|
var DEFAULT_BUILD_OPTIONS = {
|
|
2827
3312
|
analyze: false,
|
|
2828
3313
|
minify: true,
|
|
@@ -2832,27 +3317,27 @@ async function build2(options = {}) {
|
|
|
2832
3317
|
const opts = { ...DEFAULT_BUILD_OPTIONS, ...options };
|
|
2833
3318
|
const startTime = Date.now();
|
|
2834
3319
|
const rootDir = process.cwd();
|
|
2835
|
-
const outputDir =
|
|
2836
|
-
if (
|
|
2837
|
-
|
|
2838
|
-
}
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
console.log(
|
|
3320
|
+
const outputDir = path8.join(rootDir, ".float");
|
|
3321
|
+
if (fs6.existsSync(outputDir)) {
|
|
3322
|
+
fs6.rmSync(outputDir, { recursive: true });
|
|
3323
|
+
}
|
|
3324
|
+
fs6.mkdirSync(outputDir, { recursive: true });
|
|
3325
|
+
fs6.mkdirSync(path8.join(outputDir, "pages"), { recursive: true });
|
|
3326
|
+
fs6.mkdirSync(path8.join(outputDir, "static"), { recursive: true });
|
|
3327
|
+
fs6.mkdirSync(path8.join(outputDir, "server"), { recursive: true });
|
|
3328
|
+
console.log(pc3.dim(" Scanning routes..."));
|
|
2844
3329
|
const routes = await scanRoutes(rootDir);
|
|
2845
3330
|
const pageRoutes = routes.filter((r) => r.type === "page" && !r.params.length);
|
|
2846
3331
|
const dynamicRoutes = routes.filter((r) => r.type === "page" && r.params.length > 0);
|
|
2847
3332
|
const apiRoutes = routes.filter((r) => r.type === "api");
|
|
2848
|
-
console.log(
|
|
2849
|
-
console.log(
|
|
3333
|
+
console.log(pc3.dim(` Found ${pageRoutes.length} static pages, ${dynamicRoutes.length} dynamic routes, ${apiRoutes.length} API routes`));
|
|
3334
|
+
console.log(pc3.dim(" Building client bundle..."));
|
|
2850
3335
|
const clientEntryPoints = routes.filter((r) => r.type === "page").map((r) => r.absolutePath);
|
|
2851
3336
|
if (clientEntryPoints.length > 0) {
|
|
2852
3337
|
await esbuild2.build({
|
|
2853
3338
|
entryPoints: clientEntryPoints,
|
|
2854
3339
|
bundle: true,
|
|
2855
|
-
outdir:
|
|
3340
|
+
outdir: path8.join(outputDir, "static", "_float"),
|
|
2856
3341
|
format: "esm",
|
|
2857
3342
|
splitting: true,
|
|
2858
3343
|
minify: opts.minify,
|
|
@@ -2874,13 +3359,13 @@ async function build2(options = {}) {
|
|
|
2874
3359
|
metafile: opts.analyze
|
|
2875
3360
|
});
|
|
2876
3361
|
}
|
|
2877
|
-
console.log(
|
|
3362
|
+
console.log(pc3.dim(" Building server bundle..."));
|
|
2878
3363
|
const serverEntryPoints = routes.map((r) => r.absolutePath);
|
|
2879
3364
|
if (serverEntryPoints.length > 0) {
|
|
2880
3365
|
await esbuild2.build({
|
|
2881
3366
|
entryPoints: serverEntryPoints,
|
|
2882
3367
|
bundle: true,
|
|
2883
|
-
outdir:
|
|
3368
|
+
outdir: path8.join(outputDir, "server"),
|
|
2884
3369
|
format: "esm",
|
|
2885
3370
|
platform: "node",
|
|
2886
3371
|
target: ["node18"],
|
|
@@ -2897,26 +3382,26 @@ async function build2(options = {}) {
|
|
|
2897
3382
|
external: ["react", "react-dom", "@float/core"]
|
|
2898
3383
|
});
|
|
2899
3384
|
}
|
|
2900
|
-
console.log(
|
|
3385
|
+
console.log(pc3.dim(" Pre-rendering static pages..."));
|
|
2901
3386
|
const prerenderedPages = [];
|
|
2902
3387
|
for (const route of pageRoutes) {
|
|
2903
3388
|
try {
|
|
2904
3389
|
const html = await renderPage(route, {}, { isDev: false });
|
|
2905
|
-
const outputPath = route.path === "/" ?
|
|
2906
|
-
|
|
2907
|
-
|
|
3390
|
+
const outputPath = route.path === "/" ? path8.join(outputDir, "pages", "index.html") : path8.join(outputDir, "pages", route.path, "index.html");
|
|
3391
|
+
fs6.mkdirSync(path8.dirname(outputPath), { recursive: true });
|
|
3392
|
+
fs6.writeFileSync(outputPath, html);
|
|
2908
3393
|
prerenderedPages.push(route.path);
|
|
2909
|
-
console.log(
|
|
3394
|
+
console.log(pc3.dim(` \u2713 ${route.path}`));
|
|
2910
3395
|
} catch (error) {
|
|
2911
|
-
console.log(
|
|
3396
|
+
console.log(pc3.yellow(` \u26A0 ${route.path} (will render at runtime)`));
|
|
2912
3397
|
}
|
|
2913
3398
|
}
|
|
2914
|
-
console.log(
|
|
3399
|
+
console.log(pc3.dim(" Building API routes..."));
|
|
2915
3400
|
for (const route of apiRoutes) {
|
|
2916
3401
|
await esbuild2.build({
|
|
2917
3402
|
entryPoints: [route.absolutePath],
|
|
2918
3403
|
bundle: true,
|
|
2919
|
-
outfile:
|
|
3404
|
+
outfile: path8.join(outputDir, "server", "api", `${route.path.replace(/\//g, "_")}.js`),
|
|
2920
3405
|
format: "esm",
|
|
2921
3406
|
platform: "neutral",
|
|
2922
3407
|
// Edge compatible
|
|
@@ -2924,10 +3409,10 @@ async function build2(options = {}) {
|
|
|
2924
3409
|
minify: true
|
|
2925
3410
|
});
|
|
2926
3411
|
}
|
|
2927
|
-
const publicDir =
|
|
2928
|
-
if (
|
|
2929
|
-
console.log(
|
|
2930
|
-
copyDir(publicDir,
|
|
3412
|
+
const publicDir = path8.join(rootDir, "public");
|
|
3413
|
+
if (fs6.existsSync(publicDir)) {
|
|
3414
|
+
console.log(pc3.dim(" Copying public assets..."));
|
|
3415
|
+
copyDir(publicDir, path8.join(outputDir, "static"));
|
|
2931
3416
|
}
|
|
2932
3417
|
const manifest = {
|
|
2933
3418
|
version: 1,
|
|
@@ -2936,19 +3421,19 @@ async function build2(options = {}) {
|
|
|
2936
3421
|
path: r.path,
|
|
2937
3422
|
type: r.type,
|
|
2938
3423
|
filePath: r.filePath,
|
|
2939
|
-
absolutePath:
|
|
3424
|
+
absolutePath: path8.relative(rootDir, r.absolutePath),
|
|
2940
3425
|
params: r.params,
|
|
2941
3426
|
isCatchAll: r.isCatchAll,
|
|
2942
3427
|
isOptionalCatchAll: r.isOptionalCatchAll,
|
|
2943
|
-
layouts: r.layouts.map((l) =>
|
|
3428
|
+
layouts: r.layouts.map((l) => path8.relative(rootDir, l)),
|
|
2944
3429
|
prerendered: prerenderedPages.includes(r.path)
|
|
2945
3430
|
})),
|
|
2946
3431
|
staticPages: prerenderedPages,
|
|
2947
3432
|
dynamicRoutes: dynamicRoutes.map((r) => r.path),
|
|
2948
3433
|
apiRoutes: apiRoutes.map((r) => r.path)
|
|
2949
3434
|
};
|
|
2950
|
-
|
|
2951
|
-
|
|
3435
|
+
fs6.writeFileSync(
|
|
3436
|
+
path8.join(outputDir, "routes-manifest.json"),
|
|
2952
3437
|
JSON.stringify(manifest, null, 2)
|
|
2953
3438
|
);
|
|
2954
3439
|
const duration = Date.now() - startTime;
|
|
@@ -2958,16 +3443,16 @@ async function build2(options = {}) {
|
|
|
2958
3443
|
routes: routes.length,
|
|
2959
3444
|
pages: prerenderedPages.length
|
|
2960
3445
|
};
|
|
2961
|
-
|
|
2962
|
-
|
|
3446
|
+
fs6.writeFileSync(
|
|
3447
|
+
path8.join(outputDir, "build-info.json"),
|
|
2963
3448
|
JSON.stringify(buildInfo, null, 2)
|
|
2964
3449
|
);
|
|
2965
3450
|
console.log("");
|
|
2966
|
-
console.log(
|
|
2967
|
-
console.log(
|
|
2968
|
-
console.log(
|
|
2969
|
-
console.log(
|
|
2970
|
-
console.log(
|
|
3451
|
+
console.log(pc3.bold(" Build Summary:"));
|
|
3452
|
+
console.log(pc3.dim(` Static Pages: ${prerenderedPages.length}`));
|
|
3453
|
+
console.log(pc3.dim(` Dynamic Routes: ${dynamicRoutes.length}`));
|
|
3454
|
+
console.log(pc3.dim(` API Routes: ${apiRoutes.length}`));
|
|
3455
|
+
console.log(pc3.dim(` Output: .float/`));
|
|
2971
3456
|
return {
|
|
2972
3457
|
routes,
|
|
2973
3458
|
duration,
|
|
@@ -2977,15 +3462,15 @@ async function build2(options = {}) {
|
|
|
2977
3462
|
};
|
|
2978
3463
|
}
|
|
2979
3464
|
function copyDir(src, dest) {
|
|
2980
|
-
|
|
2981
|
-
const entries =
|
|
3465
|
+
fs6.mkdirSync(dest, { recursive: true });
|
|
3466
|
+
const entries = fs6.readdirSync(src, { withFileTypes: true });
|
|
2982
3467
|
for (const entry of entries) {
|
|
2983
|
-
const srcPath =
|
|
2984
|
-
const destPath =
|
|
3468
|
+
const srcPath = path8.join(src, entry.name);
|
|
3469
|
+
const destPath = path8.join(dest, entry.name);
|
|
2985
3470
|
if (entry.isDirectory()) {
|
|
2986
3471
|
copyDir(srcPath, destPath);
|
|
2987
3472
|
} else {
|
|
2988
|
-
|
|
3473
|
+
fs6.copyFileSync(srcPath, destPath);
|
|
2989
3474
|
}
|
|
2990
3475
|
}
|
|
2991
3476
|
}
|
|
@@ -2993,45 +3478,45 @@ function copyDir(src, dest) {
|
|
|
2993
3478
|
// src/server/prod-server.ts
|
|
2994
3479
|
init_esm_shims();
|
|
2995
3480
|
import http2 from "http";
|
|
2996
|
-
import
|
|
2997
|
-
import
|
|
2998
|
-
import
|
|
3481
|
+
import fs7 from "fs";
|
|
3482
|
+
import path9 from "path";
|
|
3483
|
+
import pc4 from "picocolors";
|
|
2999
3484
|
import mime2 from "mime-types";
|
|
3000
3485
|
var cachedRoutes = [];
|
|
3001
3486
|
var pageCache = /* @__PURE__ */ new Map();
|
|
3002
3487
|
async function startProductionServer(options) {
|
|
3003
3488
|
const { port, host } = options;
|
|
3004
3489
|
const rootDir = process.cwd();
|
|
3005
|
-
const distDir =
|
|
3006
|
-
const publicDir =
|
|
3007
|
-
const manifestPath =
|
|
3008
|
-
if (
|
|
3009
|
-
const manifest = JSON.parse(
|
|
3490
|
+
const distDir = path9.join(rootDir, ".float");
|
|
3491
|
+
const publicDir = path9.join(rootDir, "public");
|
|
3492
|
+
const manifestPath = path9.join(distDir, "routes-manifest.json");
|
|
3493
|
+
if (fs7.existsSync(manifestPath)) {
|
|
3494
|
+
const manifest = JSON.parse(fs7.readFileSync(manifestPath, "utf-8"));
|
|
3010
3495
|
cachedRoutes = manifest.routes;
|
|
3011
|
-
console.log(
|
|
3496
|
+
console.log(pc4.dim(` \u{1F4E6} Loaded ${cachedRoutes.length} routes from manifest`));
|
|
3012
3497
|
} else {
|
|
3013
|
-
console.error(
|
|
3498
|
+
console.error(pc4.red(" \u274C No build manifest found. Run `float build` first."));
|
|
3014
3499
|
process.exit(1);
|
|
3015
3500
|
}
|
|
3016
|
-
const pagesDir =
|
|
3017
|
-
if (
|
|
3018
|
-
const prerenderedFiles =
|
|
3501
|
+
const pagesDir = path9.join(distDir, "pages");
|
|
3502
|
+
if (fs7.existsSync(pagesDir)) {
|
|
3503
|
+
const prerenderedFiles = fs7.readdirSync(pagesDir, { recursive: true });
|
|
3019
3504
|
for (const file of prerenderedFiles) {
|
|
3020
3505
|
if (file.endsWith(".html")) {
|
|
3021
3506
|
const routePath = "/" + file.replace(/\.html$/, "").replace(/index$/, "");
|
|
3022
|
-
const content =
|
|
3507
|
+
const content = fs7.readFileSync(path9.join(pagesDir, file), "utf-8");
|
|
3023
3508
|
pageCache.set(routePath, content);
|
|
3024
3509
|
}
|
|
3025
3510
|
}
|
|
3026
|
-
console.log(
|
|
3511
|
+
console.log(pc4.dim(` \u{1F4C4} Loaded ${pageCache.size} pre-rendered pages`));
|
|
3027
3512
|
}
|
|
3028
3513
|
const server = http2.createServer(async (req, res) => {
|
|
3029
3514
|
const url = new URL(req.url || "/", `http://${host}:${port}`);
|
|
3030
3515
|
const pathname = url.pathname;
|
|
3031
3516
|
try {
|
|
3032
|
-
const staticPath =
|
|
3033
|
-
if (
|
|
3034
|
-
const content =
|
|
3517
|
+
const staticPath = path9.join(distDir, "static", pathname);
|
|
3518
|
+
if (fs7.existsSync(staticPath) && fs7.statSync(staticPath).isFile()) {
|
|
3519
|
+
const content = fs7.readFileSync(staticPath);
|
|
3035
3520
|
const contentType = mime2.lookup(staticPath) || "application/octet-stream";
|
|
3036
3521
|
res.writeHead(200, {
|
|
3037
3522
|
"Content-Type": contentType,
|
|
@@ -3040,9 +3525,9 @@ async function startProductionServer(options) {
|
|
|
3040
3525
|
res.end(content);
|
|
3041
3526
|
return;
|
|
3042
3527
|
}
|
|
3043
|
-
const publicFilePath =
|
|
3044
|
-
if (
|
|
3045
|
-
const content =
|
|
3528
|
+
const publicFilePath = path9.join(publicDir, pathname);
|
|
3529
|
+
if (fs7.existsSync(publicFilePath) && fs7.statSync(publicFilePath).isFile()) {
|
|
3530
|
+
const content = fs7.readFileSync(publicFilePath);
|
|
3046
3531
|
const contentType = mime2.lookup(publicFilePath) || "application/octet-stream";
|
|
3047
3532
|
res.writeHead(200, { "Content-Type": contentType });
|
|
3048
3533
|
res.end(content);
|
|
@@ -3075,13 +3560,13 @@ async function startProductionServer(options) {
|
|
|
3075
3560
|
});
|
|
3076
3561
|
res.end(html);
|
|
3077
3562
|
} catch (error) {
|
|
3078
|
-
console.error(
|
|
3563
|
+
console.error(pc4.red("Request error:"), error);
|
|
3079
3564
|
res.writeHead(500, { "Content-Type": "text/html" });
|
|
3080
3565
|
res.end("<h1>500 - Internal Server Error</h1>");
|
|
3081
3566
|
}
|
|
3082
3567
|
});
|
|
3083
3568
|
server.listen(port, host, () => {
|
|
3084
|
-
console.log(
|
|
3569
|
+
console.log(pc4.green(` \u2705 Production server running at ${pc4.cyan(`http://${host}:${port}`)}
|
|
3085
3570
|
`));
|
|
3086
3571
|
});
|
|
3087
3572
|
}
|
|
@@ -3093,14 +3578,14 @@ var VERSION = "2.0.4";
|
|
|
3093
3578
|
// src/cli/index.ts
|
|
3094
3579
|
var cli = cac("float");
|
|
3095
3580
|
var banner = `
|
|
3096
|
-
${
|
|
3097
|
-
${
|
|
3098
|
-
${
|
|
3099
|
-
${
|
|
3581
|
+
${pc5.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")}
|
|
3582
|
+
${pc5.cyan("\u2551")} ${pc5.bold(pc5.magenta("\u26A1 Float.js"))} ${pc5.dim(`v${VERSION}`)} ${pc5.cyan("\u2551")}
|
|
3583
|
+
${pc5.cyan("\u2551")} ${pc5.dim("Ultra Modern Web Framework")} ${pc5.cyan("\u2551")}
|
|
3584
|
+
${pc5.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D")}
|
|
3100
3585
|
`;
|
|
3101
3586
|
cli.command("dev", "Start development server").option("-p, --port <port>", "Port to listen on", { default: 3e3 }).option("-h, --host <host>", "Host to bind to", { default: "localhost" }).option("--open", "Open browser on start", { default: false }).action(async (options) => {
|
|
3102
3587
|
console.log(banner);
|
|
3103
|
-
console.log(
|
|
3588
|
+
console.log(pc5.cyan("\u{1F680} Starting development server...\n"));
|
|
3104
3589
|
try {
|
|
3105
3590
|
const server = await createDevServer({
|
|
3106
3591
|
port: Number(options.port),
|
|
@@ -3109,45 +3594,45 @@ cli.command("dev", "Start development server").option("-p, --port <port>", "Port
|
|
|
3109
3594
|
});
|
|
3110
3595
|
await server.start();
|
|
3111
3596
|
} catch (error) {
|
|
3112
|
-
console.error(
|
|
3597
|
+
console.error(pc5.red("\u274C Failed to start dev server:"), error);
|
|
3113
3598
|
process.exit(1);
|
|
3114
3599
|
}
|
|
3115
3600
|
});
|
|
3116
3601
|
cli.command("build", "Build for production").option("--analyze", "Analyze bundle size", { default: false }).action(async (options) => {
|
|
3117
3602
|
console.log(banner);
|
|
3118
|
-
console.log(
|
|
3603
|
+
console.log(pc5.cyan("\u{1F4E6} Building for production...\n"));
|
|
3119
3604
|
try {
|
|
3120
3605
|
const startTime = Date.now();
|
|
3121
3606
|
await build2({ analyze: options.analyze });
|
|
3122
3607
|
const duration = Date.now() - startTime;
|
|
3123
|
-
console.log(
|
|
3608
|
+
console.log(pc5.green(`
|
|
3124
3609
|
\u2705 Build completed in ${duration}ms`));
|
|
3125
3610
|
} catch (error) {
|
|
3126
|
-
console.error(
|
|
3611
|
+
console.error(pc5.red("\u274C Build failed:"), error);
|
|
3127
3612
|
process.exit(1);
|
|
3128
3613
|
}
|
|
3129
3614
|
});
|
|
3130
3615
|
cli.command("start", "Start production server").option("-p, --port <port>", "Port to listen on", { default: 3e3 }).option("-h, --host <host>", "Host to bind to", { default: "0.0.0.0" }).action(async (options) => {
|
|
3131
3616
|
console.log(banner);
|
|
3132
|
-
console.log(
|
|
3617
|
+
console.log(pc5.cyan("\u{1F310} Starting production server...\n"));
|
|
3133
3618
|
try {
|
|
3134
3619
|
await startProductionServer({
|
|
3135
3620
|
port: Number(options.port),
|
|
3136
3621
|
host: options.host
|
|
3137
3622
|
});
|
|
3138
3623
|
} catch (error) {
|
|
3139
|
-
console.error(
|
|
3624
|
+
console.error(pc5.red("\u274C Failed to start server:"), error);
|
|
3140
3625
|
process.exit(1);
|
|
3141
3626
|
}
|
|
3142
3627
|
});
|
|
3143
3628
|
cli.command("info", "Show environment information").action(() => {
|
|
3144
3629
|
console.log(banner);
|
|
3145
|
-
console.log(
|
|
3146
|
-
console.log(` ${
|
|
3147
|
-
console.log(` ${
|
|
3148
|
-
console.log(` ${
|
|
3149
|
-
console.log(` ${
|
|
3150
|
-
console.log(` ${
|
|
3630
|
+
console.log(pc5.bold("Environment Info:\n"));
|
|
3631
|
+
console.log(` ${pc5.dim("Float.js:")} v${VERSION}`);
|
|
3632
|
+
console.log(` ${pc5.dim("Node.js:")} ${process.version}`);
|
|
3633
|
+
console.log(` ${pc5.dim("Platform:")} ${process.platform}`);
|
|
3634
|
+
console.log(` ${pc5.dim("Arch:")} ${process.arch}`);
|
|
3635
|
+
console.log(` ${pc5.dim("CWD:")} ${process.cwd()}`);
|
|
3151
3636
|
});
|
|
3152
3637
|
cli.help();
|
|
3153
3638
|
cli.version(VERSION);
|